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