sefaz 0.2.0 → 1.4.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +121 -0
  4. data/Gemfile.lock +77 -20
  5. data/README.md +449 -17
  6. data/Rakefile +1 -0
  7. data/docs/CFe-SAT/Especificacao_SAT_v_ER_2_30_03.pdf +0 -0
  8. data/docs/CFe-SAT/Manual_Orientacao_SAT_v_MO_2_19_04.pdf +0 -0
  9. data/docs/CFe-SAT/Manual_do_Emulador_SAT-CF-e_Offline_2015_09_10.pdf +0 -0
  10. data/docs/CFe-SAT/_CANCEL_dataset.md +14 -0
  11. data/docs/CFe-SAT/_SALE_dataset.md +171 -0
  12. data/docs/CFe-SAT/emulador_off_line_v2_9_4.zip +0 -0
  13. data/docs/NFe-NFCe/ANEXO I - Leiaute e Regra de Valida/303/247/303/243o - NF-e e NFC-e.pdf +0 -0
  14. data/docs/NFe-NFCe/ANEXO II -Manual Especifica/303/247/303/265esT/303/251cnicas - Danfe-C/303/263digo-Barras.pdf +0 -0
  15. data/docs/NFe-NFCe/Anexo III - Manual de Conting/303/252ncia - NF-e.pdf +0 -0
  16. data/docs/NFe-NFCe/Anexo IV - Manual de Conting/303/252ncia - NFC-e.pdf +0 -0
  17. 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
  18. data/docs/NFe-NFCe/_dataset.md +819 -0
  19. data/lib/sefaz/assets/NFe_logo.png +0 -0
  20. data/lib/sefaz/base.rb +11 -0
  21. data/lib/sefaz/configuration.rb +66 -0
  22. data/lib/sefaz/exception.rb +14 -0
  23. data/lib/sefaz/refinement.rb +91 -0
  24. data/lib/sefaz/utils/connection.rb +35 -0
  25. data/lib/sefaz/utils/prawn_helper.rb +49 -0
  26. data/lib/sefaz/utils/signer.rb +122 -0
  27. data/lib/sefaz/version.rb +1 -1
  28. data/lib/sefaz/webservice/base.rb +13 -0
  29. data/lib/sefaz/webservice/nfe/auditor.rb +35 -0
  30. data/lib/sefaz/webservice/nfe/client.rb +471 -0
  31. data/lib/sefaz/webservice/nfe/connection.rb +17 -0
  32. data/lib/sefaz/webservice/nfe/dataset.rb +451 -0
  33. data/lib/sefaz/webservice/nfe/templates/base.rb +32 -0
  34. data/lib/sefaz/webservice/nfe/templates/evento_cancelamento.rb +48 -0
  35. data/lib/sefaz/webservice/nfe/templates/evento_carta_correcao.rb +54 -0
  36. data/lib/sefaz/webservice/nfe/templates/evento_inutilizacao.rb +22 -0
  37. data/lib/sefaz/webservice/nfe/validator.rb +63 -0
  38. data/lib/sefaz/webservice/nfe/wsdl.rb +117 -0
  39. data/lib/sefaz/webservice/sat/client.rb +44 -0
  40. data/lib/sefaz/webservice/sat/dataset/cancel.rb +34 -0
  41. data/lib/sefaz/webservice/sat/dataset/sale.rb +176 -0
  42. data/lib/sefaz/webservice/sat/templates/base.rb +54 -0
  43. data/lib/sefaz/webservice/sat/templates/cupom_fiscal_55mm.rb +307 -0
  44. data/lib/sefaz/webservice/sat/templates/cupom_fiscal_80mm.rb +307 -0
  45. data/lib/sefaz.rb +51 -3
  46. data/sefaz.gemspec +6 -3
  47. metadata +103 -9
@@ -0,0 +1,471 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Webservice
5
+ module NFE
6
+ # Principal classe de integração com o módulo NF-e/NFC-e
7
+ class Client < SEFAZ::Webservice::Base
8
+
9
+ SERVICES = %i[ setaAmbiente setaRespTecnico setaPFXTss setaPFXAss statusDoServico consultarNF consultarCadastro consultarRecibo
10
+ assinarNF validarNF auditarNF inutilizarNF exportarInutilizarNF enviarInutilizarNF calculaChaveInutilizacao
11
+ enviarEvento enviarLoteDeEvento cancelarNF exportarCancelarNF enviarCCe exportarCCe enviarNF enviarLoteNF calculaChaveNF
12
+ gerarDANFE gerarDocCancelamento gerarDocCartaCorrecao]
13
+
14
+ # Métodos de Configuração:
15
+ # - setaAmbiente
16
+ # - setaRespTecnico
17
+ # - setaPFXTss
18
+ # - setaPFXAss
19
+
20
+ # Métodos de Consulta, Validação e Documentos: (SEM ASSINATURA)
21
+ # - statusDoServico
22
+ # - consultarNF
23
+ # - consultarCadastro
24
+ # - consultarRecibo
25
+ # - consultarGTIN (PENDENTE)
26
+ # - consultarDistribuicaoDFe (PENDENTE)
27
+ # - consultarDistribuicaoDFeChave (PENDENTE)
28
+ # - validarNF
29
+ # - auditarNF
30
+
31
+ # Métodos de Manipulação: (MAIORIA EXIGE ASSINATURA)
32
+ # -- Os métodos 'enviarNF' e 'enviarLoteNF' não precisa 'exportar', pois os dados são montados externo à gema (XML ou Hash ou Dataset)
33
+ # - assinarNF
34
+ # - enviarNF
35
+ # - enviarLoteNF
36
+ # - calculaChaveNF
37
+ # - inutilizarNF
38
+ # - exportarInutilizarNF
39
+ # - enviarInutilizarNF
40
+ # - calculaChaveInutilizacao
41
+ # - cancelarNF (EVENTO)
42
+ # - exportarCancelarNF (EVENTO)
43
+ # - enviarCCe (EVENTO)
44
+ # - exportarCCe (EVENTO)
45
+ # - enviarManifestacao (PENDENTE) (EVENTO)
46
+ # - exportarManifestacao (PENDENTE) (EVENTO)
47
+ # - enviarEvento
48
+ # - enviarLoteDeEvento
49
+ # - gerarDANFE
50
+ # - gerarDANFCE (PENDENTE)
51
+ # - gerarDocCancelamento
52
+ # - gerarDocCartaCorrecao
53
+ # - gerarDocInutilizacao (PENDENTE)
54
+ # - gerarLeiauteEvento (PRIVADO)
55
+
56
+ def initialize
57
+ end
58
+
59
+ def setaAmbiente(params = {})
60
+ @uf = params[:uf]
61
+ @ambiente = params[:ambiente]
62
+ @cnpj = params[:cnpj].to_s.delete("^0-9")
63
+ end
64
+
65
+ def setaRespTecnico(params = {})
66
+ @cnpjTec = params[:cnpj]
67
+ @contatoTec = params[:contato]
68
+ @emailTec = params[:email]
69
+ @foneTec = params[:fone]
70
+ @idCSRT = params[:idCSRT]
71
+ @CSRT = params[:CSRT]
72
+ end
73
+
74
+ def setaPFXTss(params = {})
75
+ @pkcs12Tss = OpenSSL::PKCS12.new(params[:pfx], params[:senha])
76
+ end
77
+
78
+ def setaPFXAss(params = {})
79
+ @pkcs12Ass = OpenSSL::PKCS12.new(params[:pfx], params[:senha])
80
+ end
81
+
82
+ # Consulta Status SEFAZ
83
+ def statusDoServico
84
+ versao = "4.00"
85
+ hash = { consStatServ: { tpAmb: @ambiente, cUF: @uf, xServ: 'STATUS', :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao } }
86
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NfeStatusServico, @ambiente, @uf)
87
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
88
+ resp = conn.call(:nfe_status_servico_nf, hash)
89
+ return [resp.body.to_xml!, resp.body]
90
+ end
91
+
92
+ # Consulta Situação NF
93
+ # @chaveNF(String) = Chave de acesso de uma NF
94
+ def consultarNF(chaveNF)
95
+ versao = "4.00"
96
+ hash = { consSitNFe: { tpAmb: @ambiente, xServ: 'CONSULTAR', chNFe: chaveNF, :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao } }
97
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NfeConsultaProtocolo, @ambiente, @uf)
98
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
99
+ resp = conn.call(:nfe_consulta_nf, hash)
100
+ return [resp.body.to_xml!, resp.body]
101
+ end
102
+
103
+ # Consulta Cadastro
104
+ # @nroDocumento(String) = Número do documento
105
+ # @tpDocumento(String) = CNPJ/CPF/IE
106
+ # @uf(String) = Sigla do estado que será consultado (SP; MG; RJ; ...)
107
+ def consultarCadastro(nroDocumento, tpDocumento, uf)
108
+ versao = "2.00"
109
+ hash = { ConsCad: { infCons: { xServ: 'CONS-CAD', UF: uf }, :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao } }
110
+ case tpDocumento
111
+ when 'CNPJ'; hash[:ConsCad][:infCons][:CNPJ] = nroDocumento.to_s.delete("^0-9")
112
+ when 'CPF'; hash[:ConsCad][:infCons][:CPF] = nroDocumento.to_s.delete("^0-9")
113
+ when 'IE'; hash[:ConsCad][:infCons][:IE] = nroDocumento.to_s.delete("^0-9")
114
+ end
115
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NfeConsultaCadastro, @ambiente, @uf)
116
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
117
+ resp = conn.call(:consulta_cadastro, hash)
118
+ return [resp.body.to_xml!, resp.body]
119
+ end
120
+
121
+ # Consulta Recebido de Lote
122
+ # @numRecibo(String) = Número do recibo do lote de NF-e
123
+ def consultarRecibo(numRecibo)
124
+ versao = "4.00"
125
+ hash = { consReciNFe: { tpAmb: @ambiente, nRec: numRecibo, :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao } }
126
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NFeRetAutorizacao, @ambiente, @uf)
127
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
128
+ resp = conn.call(:nfe_ret_autorizacao_lote, hash)
129
+ return [resp.body.to_xml!, resp.body]
130
+ end
131
+
132
+ # Assinar NF - PFX de assinatura - Certificado A1
133
+ # @documento(Hash ou String) = XML ou HASH que será assinado
134
+ def assinarNF(documento)
135
+ xml = (documento.is_a?(Hash) ? documento.to_xml! : documento)
136
+ doc = SEFAZ::Utils::Signer.new(xml)
137
+ doc.sign!(@pkcs12Ass.certificate, @pkcs12Ass.key)
138
+ xml = doc.to_xml
139
+ return [xml, xml.to_hash!]
140
+ end
141
+
142
+ # Enviar NF - Necessário uma NF assinada
143
+ # @documento(Hash ou String) = XML ou HASH assinado que será enviado
144
+ # @indSinc(String) = "0"=Assíncrono / "1"=Síncrono
145
+ # @idLote(String) = Identificador de controle do Lote de envio do Lote
146
+ def enviarNF(documento, indSinc, idLote)
147
+ return enviarLoteNF([ documento ], indSinc, idLote)
148
+ end
149
+
150
+ # Envia Lote de NF - Necessário que cada NF esteja assinada
151
+ # OBS: Recomendado para envio em lote de NF, cada elemento do Array pode ser Hash ou XML assinados
152
+ # @lote(Array) = Array de NF assinadas
153
+ # @indSinc(String) = "0"=Assíncrono / "1"=Síncrono
154
+ # @idLote(String) = Identificador de controle do Lote de envio do Lote
155
+ # Exemplo de @lote:
156
+ # @nf1_xml, @nf1_hash = @webservice.assinarNF(...)
157
+ # @nf2_xml, @nf2_hash = @webservice.assinarNF(...)
158
+ # @lote = [ @nf1_xml, @nf2_hash ]
159
+ def enviarLoteNF(lote, indSinc, idLote)
160
+ versao = "4.00"
161
+ lote = (lote.map { |el| el.is_a?(Hash) ? el[:NFe] : el.to_hash![:NFe] })
162
+ hash = {
163
+ enviNFe: { :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao,
164
+ idLote: idLote,
165
+ indSinc: indSinc,
166
+ NFe: lote
167
+ }
168
+ }
169
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NFeAutorizacao, @ambiente, @uf)
170
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
171
+ resp = conn.call(:nfe_autorizacao_lote, hash)
172
+ return [resp.body.to_xml!, resp.body]
173
+ end
174
+
175
+ # Calcular Chave NF
176
+ # @uf = Código da UF do emitente do Documento Fiscal
177
+ # @aamm = Ano e Mês de emissão da NF-e
178
+ # @cnpj = CNPJ do emitente
179
+ # @modelo = Modelo do Documento Fiscal (55 ou 65)
180
+ # @serie = Série do Documento Fiscal
181
+ # @nNF = Número do Documento Fiscal
182
+ # @tpEmis = Forma de emissão da NF-e
183
+ # @cNF = Código Numérico que compõe a Chave de Acesso (ID do sistema)
184
+ def calculaChaveNF(uf, aamm, cnpj, modelo, serie, nNF, tpEmis, cNF)
185
+ uf = uf.to_s.rjust(2, "0")
186
+ aamm = aamm.to_s.rjust(4, "0")
187
+ cnpj = cnpj.to_s.delete("^0-9").rjust(14, "0")
188
+ modelo = modelo.to_s.rjust(2, "0")
189
+ serie = serie.to_s.rjust(3, "0")
190
+ nNF = nNF.to_s.rjust(9, "0")
191
+ cNF = cNF.to_s.rjust(8, "0")
192
+ nChave = "#{uf[0,2]}#{aamm[0,4]}#{cnpj[0,14]}#{modelo[0,2]}#{serie[0,3]}#{nNF[0,9]}#{tpEmis[0,1]}#{cNF[0,8]}"
193
+ nPesos = "4329876543298765432987654329876543298765432"
194
+ cDV = 11 - (nChave.split("").each_with_index.map { |n, index| n.to_i * nPesos[index].to_i }.sum % 11)
195
+ return ["#{nChave}#{cDV}", cDV]
196
+ end
197
+
198
+ # Valida NF no SEFAZ RS NF-e (https://www.sefaz.rs.gov.br/NFE/NFE-VAL.aspx)
199
+ # @documento(Hash ou String) = XML ou HASH que será validado
200
+ # @openTimeout(Integer) = Tempo de espera em segundos para abrir conexão (opcional: padrão 60)
201
+ # @readTimeout(Integer) = Tempo de espera em segundos para ler resposta (opcional: padrão 60)
202
+ def validarNF(documento, openTimeout = 60, readTimeout = 60)
203
+ xml = (documento.is_a?(Hash) ? documento.to_xml! : documento)
204
+ validator = SEFAZ::Webservice::NFE::Validator.new(xml)
205
+ stat, msg, err = validator.exec(openTimeout, readTimeout)
206
+ case stat
207
+ when :ok; return [true, msg, err]
208
+ else return [false, {}, {}]
209
+ end
210
+ end
211
+
212
+ # Auditar NF no validador TecnoSpeed (https://validador.nfe.tecnospeed.com.br/)
213
+ # @documento(Hash ou String) = XML ou HASH que será auditado
214
+ # @openTimeout(Integer) = Tempo de espera em segundos para abrir conexão (opcional: padrão 60)
215
+ # @readTimeout(Integer) = Tempo de espera em segundos para ler resposta (opcional: padrão 60)
216
+ def auditarNF(documento, openTimeout = 60, readTimeout = 60)
217
+ xml = (documento.is_a?(Hash) ? documento.to_xml! : documento)
218
+ auditor = SEFAZ::Webservice::NFE::Auditor.new(xml)
219
+ stat, msg = auditor.exec(openTimeout, readTimeout)
220
+ case stat
221
+ when :ok; return [true, msg]
222
+ else return [false, {}]
223
+ end
224
+ end
225
+
226
+ # Inutilizar NF - Gera, assina e envia o documento com certificado A1 (exportarInutilizarNF, assinarNF, enviarInutilizarNF)
227
+ # OBS: Caso parâmetro @chaveInut estiver em branco, a chave será calculada automaticamente (calculaChaveInutilizacao)
228
+ # @chaveInut = Identificador da TAG a ser assinada
229
+ # @ano = Ano de inutilização da numeração
230
+ # @modelo = Modelo do documento (55 ou 65)
231
+ # @serie = Série da NF-e
232
+ # @nroNFIni = Número da NF-e inicial a ser inutilizada
233
+ # @nroNFFin = Número da NF-e final a ser inutilizada
234
+ # @justificativa = Informar a justificativa do pedido de inutilização
235
+ def inutilizarNF(chaveInut, ano, modelo, serie, nroNFIni, nroNFFin, justificativa)
236
+ _, hash = exportarInutilizarNF(chaveInut, ano, modelo, serie, nroNFIni, nroNFFin, justificativa)
237
+ _, hash = assinarNF(hash)
238
+ return enviarInutilizarNF(hash)
239
+ end
240
+
241
+ # Calcular Chave de Inutilização
242
+ # @ano = Ano de inutilização da numeração
243
+ # @cnpj = CNPJ do emitente
244
+ # @modelo = Modelo do documento (55 ou 65)
245
+ # @serie = Série da NF-e
246
+ # @nroNFIni = Número da NF-e inicial a ser inutilizada
247
+ # @nroNFFin = Número da NF-e final a ser inutilizada
248
+ def calculaChaveInutilizacao(ano, cnpj, modelo, serie, nroNFIni, nroNFFin)
249
+ serie = serie.to_s.rjust(3, "0")
250
+ nroNFIni = nroNFIni.to_s.rjust(9, "0")
251
+ nroNFFin = nroNFFin.to_s.rjust(9, "0")
252
+ return "ID#{@uf}#{ano}#{cnpj}#{modelo}#{serie}#{nroNFIni}#{nroNFFin}"
253
+ end
254
+
255
+ # Exportar Inutilização NF - Exporta um documento bruto (sem assinatura)
256
+ # OBS: Recomendado quando utilizado o certificado A3
257
+ # Caso parâmetro @chaveInut estiver em branco, a chave será calculada automaticamente (calculaChaveInutilizacao)
258
+ # @chaveInut = Identificador da TAG a ser assinada
259
+ # @ano = Ano de inutilização da numeração
260
+ # @modelo = Modelo do documento (55 ou 65)
261
+ # @serie = Série da NF-e
262
+ # @nroNFIni = Número da NF-e inicial a ser inutilizada
263
+ # @nroNFFin = Número da NF-e final a ser inutilizada
264
+ # @justificativa = Informar a justificativa do pedido de inutilização
265
+ def exportarInutilizarNF(chaveInut, ano, modelo, serie, nroNFIni, nroNFFin, justificativa)
266
+ versao = "4.00"
267
+ chaveInut = calculaChaveInutilizacao(ano, @cnpj, modelo, serie, nroNFIni, nroNFFin) if chaveInut.blank?
268
+ hash = { inutNFe: { :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao, infInut: {
269
+ :@Id => chaveInut,
270
+ tpAmb: @ambiente,
271
+ xServ: 'INUTILIZAR',
272
+ cUF: @uf,
273
+ ano: ano,
274
+ CNPJ: @cnpj,
275
+ mod: modelo,
276
+ serie: serie,
277
+ nNFIni: nroNFIni,
278
+ nNFFin: nroNFFin,
279
+ xJust: justificativa
280
+ } } }
281
+ return [hash.to_xml!, hash]
282
+ end
283
+
284
+ # Enviar Inutilização NF - Necessário um documento assinado
285
+ # OBS: Recomendado quando utilizado o certificado A3
286
+ # @documento(Hash ou String) = XML ou HASH assinado que será enviado
287
+ def enviarInutilizarNF(documento)
288
+ versao = "4.00"
289
+ hash = (documento.is_a?(Hash) ? documento : documento.to_hash!)
290
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:NfeInutilizacao, @ambiente, @uf)
291
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
292
+ resp = conn.call(:nfe_inutilizacao_nf, hash)
293
+ return [resp.body.to_xml!, resp.body]
294
+ end
295
+
296
+ # Cancelar NF - Gera, assina e envia o documento com certificado A1 (exportarCancelarNF, assinarNF, enviarEvento)
297
+ # @chaveNF = Chave de acesso de uma NF
298
+ # @sequenciaEvento = O número do evento
299
+ # @dataHoraEvento = Data e Hora da Emissão do Evento (ex: 2023-01-15T17:23:00+03:00)
300
+ # @numProtocolo = Número do Protocolo de registro da NF
301
+ # @justificativa = Motivo do cancelamento da NF
302
+ # @idLote = Número de controle interno
303
+ def cancelarNF(chaveNF, sequenciaEvento, dataHoraEvento, numProtocolo, justificativa, idLote)
304
+ _, hash = exportarCancelarNF(chaveNF, sequenciaEvento, dataHoraEvento, numProtocolo, justificativa)
305
+ _, hash = assinarNF(hash)
306
+ return enviarEvento(hash, idLote)
307
+ end
308
+
309
+ # Exportar Cancelar NF - Exporta um documento bruto (sem assinatura)
310
+ # OBS: Recomendado quando utilizado o certificado A3
311
+ # @chaveNF = Chave de acesso de uma NF
312
+ # @sequenciaEvento = O número do evento
313
+ # @dataHoraEvento = Data e Hora da Emissão do Evento (ex: 2023-01-15T17:23:00+03:00)
314
+ # @numProtocolo = Número do Protocolo de registro da NF
315
+ # @justificativa = Motivo do cancelamento da NF
316
+ # @idLote = Número de controle interno
317
+ def exportarCancelarNF(chaveNF, sequenciaEvento, dataHoraEvento, numProtocolo, justificativa)
318
+ versao = "1.00"
319
+ tpEvento = "110111"
320
+ _, hash = gerarLeiauteEvento(versao, tpEvento, chaveNF, sequenciaEvento, dataHoraEvento)
321
+ hash[:evento][:infEvento][:detEvento] = { :@versao => versao,
322
+ descEvento: "Cancelamento",
323
+ nProt: numProtocolo,
324
+ xJust: justificativa
325
+ }
326
+ return [hash.to_xml!, hash]
327
+ end
328
+
329
+ # Enviar CCe - Gera, assina e envia o documento com certificado A1 (exportarCCe, assinarNF, enviarEvento)
330
+ # @chaveNF = Chave de acesso de uma NF
331
+ # @sequenciaEvento = O número do evento
332
+ # @dataHoraEvento = Data e Hora da Emissão do Evento (ex: 2023-01-15T17:23:00+03:00)
333
+ # @textoCorrecao = Motivo do cancelamento da NF
334
+ # @idLote = Número de controle interno
335
+ def enviarCCe(chaveNF, sequenciaEvento, dataHoraEvento, textoCorrecao, idLote)
336
+ _, hash = exportarCCe(chaveNF, sequenciaEvento, dataHoraEvento, textoCorrecao)
337
+ _, hash = assinarNF(hash)
338
+ return enviarEvento(hash, idLote)
339
+ end
340
+
341
+ # Exportar CCe - Exporta um documento bruto (sem assinatura)
342
+ # OBS: Recomendado quando utilizado o certificado A3
343
+ # @chaveNF = Chave de acesso de uma NF
344
+ # @sequenciaEvento = O número do evento
345
+ # @dataHoraEvento = Data e Hora da Emissão do Evento (ex: 2023-01-15T17:23:00+03:00)
346
+ # @textoCorrecao = Motivo do cancelamento da NF
347
+ def exportarCCe(chaveNF, sequenciaEvento, dataHoraEvento, textoCorrecao)
348
+ versao = "1.00"
349
+ tpEvento = "110110"
350
+ _, hash = gerarLeiauteEvento(versao, tpEvento, chaveNF, sequenciaEvento, dataHoraEvento)
351
+ hash[:evento][:infEvento][:detEvento] = { :@versao => versao,
352
+ descEvento: "Carta de Correcao",
353
+ xCorrecao: textoCorrecao,
354
+ xCondUso: "A Carta de Correcao e disciplinada pelo paragrafo 1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; III - a data de emissao ou de saida."
355
+ }
356
+ return [hash.to_xml!, hash]
357
+ end
358
+
359
+ # Enviar Evento - Necessário um documento assinado
360
+ # OBS: Recomendado quando utilizado o certificado A3
361
+ # @evento(Hash ou String) = XML ou HASH assinado que será enviado
362
+ # @idLote(String) = Identificador de controle do Lote de envio do Evento
363
+ def enviarEvento(evento, idLote)
364
+ return enviarLoteDeEvento([ evento ], idLote)
365
+ end
366
+
367
+ # Envia Lote de Eventos - Necessário que cada evento esteja assinado
368
+ # OBS: Recomendado quando utilizado o certificado A3 e/ou para envio em lote de eventos
369
+ # Cada elemento do Array pode ser Hash ou XML assinados
370
+ # @lote(Array) = Array de eventos assinados
371
+ # @idLote(String) = Identificador de controle do Lote de envio do Evento
372
+ # Exemplo de @lote:
373
+ # @eve1_xml, @eve1_hash = @webservice.exportarCancelarNF(...)
374
+ # @eve2_xml, @eve2_hash = @webservice.exportarCancelarNF(...)
375
+ # @lote = [ @eve1_xml, @eve2_hash ]
376
+ def enviarLoteDeEvento(lote, idLote)
377
+ versao = "1.00"
378
+ lote = (lote.map { |el| el.is_a?(Hash) ? el[:evento] : el.to_hash![:evento] })
379
+ hash = {
380
+ envEvento: { :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao,
381
+ idLote: idLote,
382
+ evento: lote
383
+ }
384
+ }
385
+ wsdl = SEFAZ::Webservice::NFE::WSDL.get(:RecepcaoEvento, @ambiente, @uf)
386
+ conn = SEFAZ::Webservice::NFE::Connection.new(@pkcs12Tss, wsdl, versao, @uf)
387
+ resp = conn.call(:nfe_recepcao_evento, hash)
388
+ return [resp.body.to_xml!, resp.body]
389
+ end
390
+
391
+ # Gerar DANFE - Gera o Documento Auxiliar da Nota Fiscal Eletrônica (DANFE) em PDF
392
+ # @documento(Hash ou String) = XML ou HASH que será tratado
393
+ # @logo_path(String) = Caminho do arquivo de logo (opcional)
394
+ # @logo_dimensions(Hash) = Dimensões do logo (opcional) Exemplo: { width: 100, height: 100 }
395
+ def gerarDANFE(documento, logo_path = nil, logo_dimensions = nil)
396
+ xml = (documento.is_a?(Hash) ? documento.to_xml! : documento)
397
+ doc = BrDanfe::Danfe.new(xml)
398
+
399
+ if Configuration.default.nfe_default_logotipo_enabled
400
+ doc.options.logo = Configuration.default.nfe_default_logotipo_path
401
+ doc.options.logo_dimensions = Configuration.default.nfe_default_logotipo_dimensions
402
+ end
403
+
404
+ doc.options.logo = logo_path if logo_path
405
+ doc.options.logo_dimensions = logo_dimensions if logo_dimensions
406
+ return doc.render_pdf
407
+ end
408
+
409
+ # Gerar Documento de Cancelamento em PDF
410
+ # @documento(Hash ou String) = XML ou HASH que será tratado
411
+ def gerarDocCancelamento(documento)
412
+ hash = (documento.is_a?(Hash) ? documento : documento.to_hash!)
413
+ SEFAZ::Webservice::NFE::Templates::EventoCancelamento.new.render(hash)
414
+ end
415
+
416
+ # Gerar Documento de Carta de Correção Eletrônica (CCe) em PDF
417
+ # @documento(Hash ou String) = XML ou HASH que será tratado
418
+ def gerarDocCartaCorrecao(documento)
419
+ hash = (documento.is_a?(Hash) ? documento : documento.to_hash!)
420
+ SEFAZ::Webservice::NFE::Templates::EventoCartaCorrecao.new.render(hash)
421
+ end
422
+
423
+ # Gerar Documento de Inutilização em PDF
424
+ # @documento(Hash ou String) = XML ou HASH que será tratado
425
+ def gerarDocInutilizacao(documento)
426
+ hash = (documento.is_a?(Hash) ? documento : documento.to_hash!)
427
+ SEFAZ::Webservice::NFE::Templates::EventoInutilizacao.new.render(hash)
428
+ end
429
+
430
+ # Gera Informações do Responsável Técnico - Calcula o hashCSRT e cria o grupo do responsável técnico
431
+ # Necessário quando estiver emitindo uma NF-e/NFC-e
432
+ # @documento(Hash ou String) = XML ou HASH que será tratado
433
+ def gerarInfRespTec(documento)
434
+ hash = (documento.is_a?(Hash) ? documento : documento.to_hash!)
435
+ chaveNF = hash[:NFe][:infNFe][:@Id].to_s.delete("^0-9")
436
+ concat = @CSRT.to_s + chaveNF.to_s
437
+ hexdigest = Digest::SHA1.hexdigest(concat)
438
+ hashCSRT = Base64.strict_encode64(hexdigest)
439
+ infRespTec = { infRespTec: { CNPJ: @cnpjTec, xContato: @contatoTec, email: @emailTec, fone: @foneTec, idCSRT: @idCSRT, hashCSRT: hashCSRT } }.compress!
440
+ hash[:NFe][:infNFe][:infRespTec] = infRespTec[:infRespTec]
441
+ return [hash.to_xml!, hash]
442
+ end
443
+
444
+ private
445
+
446
+ # Gera o Leiaute Mensagem de Entrada (Parte Geral) dos Eventos
447
+ # Utilizado internamente nos métodos: exportarCancelarNF, exportarCCe, exportarManifestacao, ... (métodos de eventos)
448
+ def gerarLeiauteEvento(verEvento, tpEvento, chaveNF, sequenciaEvento, dataHoraEvento)
449
+ versao = "1.00"
450
+ id = "ID" + tpEvento.to_s + chaveNF.to_s + sequenciaEvento.to_s.rjust(2, "0")
451
+ hash = {
452
+ evento: { :@xmlns => "http://www.portalfiscal.inf.br/nfe", :@versao => versao,
453
+ infEvento: { :@Id => id,
454
+ cOrgao: @uf,
455
+ tpAmb: @ambiente,
456
+ CNPJ: @cnpj,
457
+ chNFe: chaveNF,
458
+ dhEvento: dataHoraEvento,
459
+ tpEvento: tpEvento,
460
+ nSeqEvento: sequenciaEvento,
461
+ verEvento: verEvento
462
+ }
463
+ }
464
+ }
465
+ return [hash.to_xml!, hash]
466
+ end
467
+
468
+ end
469
+ end
470
+ end
471
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Webservice
5
+ module NFE
6
+ # Principal classe de conexão SOAP com o módulo NF-e/NFC-e
7
+ class Connection < SEFAZ::Utils::Connection
8
+
9
+ def initialize(pkcs12, wsdl, versaoDados, cUF)
10
+ @soap_header = { nfeCabecMsg: { versaoDados: versaoDados, cUF: cUF, :@xmlns => 'http://www.portalfiscal.inf.br/nfe' } }
11
+ super(pkcs12, wsdl, @soap_header)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end