focus_nfe 1.0.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.
- checksums.yaml +7 -0
- data/.git-hooks/pre_push/steep.rb +18 -0
- data/.git-hooks/pre_push/yard_doc.rb +18 -0
- data/.gitattributes +1 -0
- data/.overcommit.yml +69 -0
- data/.rspec +3 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +77 -0
- data/CLAUDE.md +118 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +348 -0
- data/Rakefile +105 -0
- data/data/schemas/schema_cte.json +2793 -0
- data/data/schemas/schema_cte_os.json +1335 -0
- data/data/schemas/schema_cte_os_transporte_rodoviario.json +109 -0
- data/data/schemas/schema_cte_transporte_aereo.json +115 -0
- data/data/schemas/schema_cte_transporte_aquaviario.json +174 -0
- data/data/schemas/schema_cte_transporte_dutoviario.json +65 -0
- data/data/schemas/schema_cte_transporte_ferroviario.json +144 -0
- data/data/schemas/schema_cte_transporte_multimodal.json +45 -0
- data/data/schemas/schema_cte_transporte_rodoviario.json +78 -0
- data/data/schemas/schema_dce.json +549 -0
- data/data/schemas/schema_mdfe.json +1102 -0
- data/data/schemas/schema_mdfe_transporte_aereo.json +44 -0
- data/data/schemas/schema_mdfe_transporte_aquaviario.json +209 -0
- data/data/schemas/schema_mdfe_transporte_ferroviario.json +99 -0
- data/data/schemas/schema_mdfe_transporte_rodoviario.json +628 -0
- data/data/schemas/schema_nfcom.json +1859 -0
- data/data/schemas/schema_nfe.json +4750 -0
- data/data/schemas/schema_nfe_forma_pagamento.json +97 -0
- data/data/schemas/schema_nfe_item.json +2574 -0
- data/data/schemas/schema_nfgas.json +2316 -0
- data/data/schemas/schema_nfse_nacional.json +1847 -0
- data/data/schemas/schema_nfse_recebida.json +548 -0
- data/lib/focus_nfe/client.rb +162 -0
- data/lib/focus_nfe/configuration.rb +104 -0
- data/lib/focus_nfe/errors.rb +123 -0
- data/lib/focus_nfe/esquemas/campo.rb +171 -0
- data/lib/focus_nfe/esquemas/catalogo.rb +34 -0
- data/lib/focus_nfe/esquemas/decimal.rb +66 -0
- data/lib/focus_nfe/esquemas/esquema.rb +72 -0
- data/lib/focus_nfe/esquemas/validador.rb +87 -0
- data/lib/focus_nfe/http/adapter.rb +25 -0
- data/lib/focus_nfe/http/adapters/net_http.rb +99 -0
- data/lib/focus_nfe/http/authentication.rb +23 -0
- data/lib/focus_nfe/http/connection.rb +118 -0
- data/lib/focus_nfe/http/logging.rb +100 -0
- data/lib/focus_nfe/http/response.rb +75 -0
- data/lib/focus_nfe/modelos/documento.rb +113 -0
- data/lib/focus_nfe/modelos/inutilizacao.rb +75 -0
- data/lib/focus_nfe/modelos/pagina.rb +54 -0
- data/lib/focus_nfe/recursos/backups.rb +17 -0
- data/lib/focus_nfe/recursos/base.rb +91 -0
- data/lib/focus_nfe/recursos/ceps.rb +16 -0
- data/lib/focus_nfe/recursos/cfops.rb +16 -0
- data/lib/focus_nfe/recursos/cnaes.rb +16 -0
- data/lib/focus_nfe/recursos/cnpjs.rb +13 -0
- data/lib/focus_nfe/recursos/concerns/baixavel.rb +41 -0
- data/lib/focus_nfe/recursos/concerns/baixavel_eventos.rb +34 -0
- data/lib/focus_nfe/recursos/concerns/cancelavel.rb +25 -0
- data/lib/focus_nfe/recursos/concerns/conciliavel.rb +66 -0
- data/lib/focus_nfe/recursos/concerns/consultavel.rb +26 -0
- data/lib/focus_nfe/recursos/concerns/corrigivel.rb +45 -0
- data/lib/focus_nfe/recursos/concerns/corrigivel_cte.rb +60 -0
- data/lib/focus_nfe/recursos/concerns/emitivel.rb +51 -0
- data/lib/focus_nfe/recursos/concerns/enviavel.rb +40 -0
- data/lib/focus_nfe/recursos/concerns/eventavel.rb +46 -0
- data/lib/focus_nfe/recursos/concerns/inutilizavel.rb +96 -0
- data/lib/focus_nfe/recursos/concerns/listavel.rb +22 -0
- data/lib/focus_nfe/recursos/concerns/localizavel.rb +22 -0
- data/lib/focus_nfe/recursos/concerns/notificavel.rb +23 -0
- data/lib/focus_nfe/recursos/concerns/removivel.rb +20 -0
- data/lib/focus_nfe/recursos/concerns/visualizavel.rb +28 -0
- data/lib/focus_nfe/recursos/cte.rb +35 -0
- data/lib/focus_nfe/recursos/cte_os.rb +29 -0
- data/lib/focus_nfe/recursos/ctes_recebidas.rb +38 -0
- data/lib/focus_nfe/recursos/dce.rb +16 -0
- data/lib/focus_nfe/recursos/emails_bloqueados.rb +31 -0
- data/lib/focus_nfe/recursos/empresas.rb +35 -0
- data/lib/focus_nfe/recursos/mdfe.rb +78 -0
- data/lib/focus_nfe/recursos/municipios.rb +60 -0
- data/lib/focus_nfe/recursos/ncms.rb +16 -0
- data/lib/focus_nfe/recursos/nfce.rb +19 -0
- data/lib/focus_nfe/recursos/nfcom.rb +16 -0
- data/lib/focus_nfe/recursos/nfe.rb +106 -0
- data/lib/focus_nfe/recursos/nfes_recebidas.rb +56 -0
- data/lib/focus_nfe/recursos/nfgas.rb +16 -0
- data/lib/focus_nfe/recursos/nfse.rb +18 -0
- data/lib/focus_nfe/recursos/nfse_nacional.rb +16 -0
- data/lib/focus_nfe/recursos/nfses_nacionais_recebidas.rb +21 -0
- data/lib/focus_nfe/recursos/webhooks.rb +22 -0
- data/lib/focus_nfe/version.rb +6 -0
- data/lib/focus_nfe/webhook.rb +68 -0
- data/lib/focus_nfe.rb +124 -0
- data/sig/focus_nfe/client.rbs +38 -0
- data/sig/focus_nfe/configuration.rbs +29 -0
- data/sig/focus_nfe/errors.rbs +59 -0
- data/sig/focus_nfe/esquemas/campo.rbs +47 -0
- data/sig/focus_nfe/esquemas/catalogo.rbs +8 -0
- data/sig/focus_nfe/esquemas/decimal.rbs +25 -0
- data/sig/focus_nfe/esquemas/esquema.rbs +30 -0
- data/sig/focus_nfe/esquemas/validador.rbs +20 -0
- data/sig/focus_nfe/http/adapter.rbs +7 -0
- data/sig/focus_nfe/http/adapters/net_http.rbs +25 -0
- data/sig/focus_nfe/http/authentication.rbs +9 -0
- data/sig/focus_nfe/http/connection.rbs +32 -0
- data/sig/focus_nfe/http/logging.rbs +30 -0
- data/sig/focus_nfe/http/response.rbs +28 -0
- data/sig/focus_nfe/modelos/documento.rbs +34 -0
- data/sig/focus_nfe/modelos/inutilizacao.rbs +24 -0
- data/sig/focus_nfe/modelos/pagina.rbs +21 -0
- data/sig/focus_nfe/recursos/backups.rbs +7 -0
- data/sig/focus_nfe/recursos/base.rbs +25 -0
- data/sig/focus_nfe/recursos/ceps.rbs +10 -0
- data/sig/focus_nfe/recursos/cfops.rbs +10 -0
- data/sig/focus_nfe/recursos/cnaes.rbs +10 -0
- data/sig/focus_nfe/recursos/cnpjs.rbs +7 -0
- data/sig/focus_nfe/recursos/concerns/baixavel.rbs +12 -0
- data/sig/focus_nfe/recursos/concerns/baixavel_eventos.rbs +14 -0
- data/sig/focus_nfe/recursos/concerns/cancelavel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/conciliavel.rbs +15 -0
- data/sig/focus_nfe/recursos/concerns/consultavel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/corrigivel.rbs +15 -0
- data/sig/focus_nfe/recursos/concerns/corrigivel_cte.rbs +17 -0
- data/sig/focus_nfe/recursos/concerns/emitivel.rbs +14 -0
- data/sig/focus_nfe/recursos/concerns/enviavel.rbs +15 -0
- data/sig/focus_nfe/recursos/concerns/eventavel.rbs +12 -0
- data/sig/focus_nfe/recursos/concerns/inutilizavel.rbs +20 -0
- data/sig/focus_nfe/recursos/concerns/listavel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/localizavel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/notificavel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/removivel.rbs +9 -0
- data/sig/focus_nfe/recursos/concerns/visualizavel.rbs +9 -0
- data/sig/focus_nfe/recursos/cte.rbs +17 -0
- data/sig/focus_nfe/recursos/cte_os.rbs +17 -0
- data/sig/focus_nfe/recursos/ctes_recebidas.rbs +14 -0
- data/sig/focus_nfe/recursos/dce.rbs +10 -0
- data/sig/focus_nfe/recursos/emails_bloqueados.rbs +12 -0
- data/sig/focus_nfe/recursos/empresas.rbs +12 -0
- data/sig/focus_nfe/recursos/mdfe.rbs +21 -0
- data/sig/focus_nfe/recursos/municipios.rbs +19 -0
- data/sig/focus_nfe/recursos/ncms.rbs +10 -0
- data/sig/focus_nfe/recursos/nfce.rbs +12 -0
- data/sig/focus_nfe/recursos/nfcom.rbs +10 -0
- data/sig/focus_nfe/recursos/nfe.rbs +23 -0
- data/sig/focus_nfe/recursos/nfes_recebidas.rbs +16 -0
- data/sig/focus_nfe/recursos/nfgas.rbs +10 -0
- data/sig/focus_nfe/recursos/nfse.rbs +11 -0
- data/sig/focus_nfe/recursos/nfse_nacional.rbs +10 -0
- data/sig/focus_nfe/recursos/nfses_nacionais_recebidas.rbs +11 -0
- data/sig/focus_nfe/recursos/webhooks.rbs +11 -0
- data/sig/focus_nfe/webhook.rbs +11 -0
- data/sig/focus_nfe.rbs +10 -0
- data/spec/focus_nfe/client_spec.rb +208 -0
- data/spec/focus_nfe/configuration_spec.rb +121 -0
- data/spec/focus_nfe/errors_mapping_spec.rb +68 -0
- data/spec/focus_nfe/errors_spec.rb +107 -0
- data/spec/focus_nfe/esquemas/campo_spec.rb +291 -0
- data/spec/focus_nfe/esquemas/decimal_spec.rb +54 -0
- data/spec/focus_nfe/esquemas/esquema_spec.rb +73 -0
- data/spec/focus_nfe/esquemas/validador_spec.rb +167 -0
- data/spec/focus_nfe/esquemas_spec.rb +42 -0
- data/spec/focus_nfe/http/adapter_spec.rb +8 -0
- data/spec/focus_nfe/http/adapters/net_http_spec.rb +181 -0
- data/spec/focus_nfe/http/authentication_spec.rb +24 -0
- data/spec/focus_nfe/http/connection_spec.rb +255 -0
- data/spec/focus_nfe/http/logging_spec.rb +83 -0
- data/spec/focus_nfe/http/response_spec.rb +161 -0
- data/spec/focus_nfe/modelos/documento_spec.rb +150 -0
- data/spec/focus_nfe/modelos/inutilizacao_spec.rb +91 -0
- data/spec/focus_nfe/modelos/pagina_spec.rb +77 -0
- data/spec/focus_nfe/recursos/backups_spec.rb +29 -0
- data/spec/focus_nfe/recursos/base_spec.rb +56 -0
- data/spec/focus_nfe/recursos/ceps_spec.rb +16 -0
- data/spec/focus_nfe/recursos/cfops_spec.rb +16 -0
- data/spec/focus_nfe/recursos/cnaes_spec.rb +20 -0
- data/spec/focus_nfe/recursos/cnpjs_spec.rb +11 -0
- data/spec/focus_nfe/recursos/concerns/emitivel_validacao_spec.rb +158 -0
- data/spec/focus_nfe/recursos/cte_os_spec.rb +9 -0
- data/spec/focus_nfe/recursos/cte_spec.rb +9 -0
- data/spec/focus_nfe/recursos/ctes_recebidas_spec.rb +56 -0
- data/spec/focus_nfe/recursos/dce_spec.rb +8 -0
- data/spec/focus_nfe/recursos/emails_bloqueados_spec.rb +29 -0
- data/spec/focus_nfe/recursos/empresas_spec.rb +45 -0
- data/spec/focus_nfe/recursos/mdfe_spec.rb +100 -0
- data/spec/focus_nfe/recursos/municipios_spec.rb +58 -0
- data/spec/focus_nfe/recursos/ncms_spec.rb +16 -0
- data/spec/focus_nfe/recursos/nfce_spec.rb +10 -0
- data/spec/focus_nfe/recursos/nfcom_spec.rb +8 -0
- data/spec/focus_nfe/recursos/nfe_spec.rb +262 -0
- data/spec/focus_nfe/recursos/nfes_recebidas_spec.rb +87 -0
- data/spec/focus_nfe/recursos/nfgas_spec.rb +8 -0
- data/spec/focus_nfe/recursos/nfse_nacional_spec.rb +8 -0
- data/spec/focus_nfe/recursos/nfse_spec.rb +9 -0
- data/spec/focus_nfe/recursos/nfses_nacionais_recebidas_spec.rb +17 -0
- data/spec/focus_nfe/recursos/webhooks_spec.rb +22 -0
- data/spec/focus_nfe/webhook_spec.rb +66 -0
- data/spec/focus_nfe_global_configuration_spec.rb +70 -0
- data/spec/focus_nfe_require_spec.rb +87 -0
- data/spec/focus_nfe_spec.rb +11 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/support/shared_examples/recurso_fiscal.rb +445 -0
- data/spec/support/shared_examples/recurso_leitura.rb +217 -0
- data/tools/pull_fields.rb +62 -0
- metadata +420 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.shared_examples "um recurso emitível" do |caminho|
|
|
4
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
5
|
+
|
|
6
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: environment) }
|
|
7
|
+
let(:environment) { :homologacao }
|
|
8
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
9
|
+
let(:dados) { { "natureza_operacao" => "Venda" } }
|
|
10
|
+
let(:processando) { '{"status":"processando_autorizacao"}' }
|
|
11
|
+
|
|
12
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
13
|
+
|
|
14
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
15
|
+
def producao = "https://api.focusnfe.com.br"
|
|
16
|
+
|
|
17
|
+
def stub_recurso(verb, path, host: homologacao, status: 200, body: "{}")
|
|
18
|
+
stub_request(verb, "#{host}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "#emitir" do
|
|
22
|
+
before { stub_recurso(:post, "#{caminho}?ref=pedido-42", status: 202, body: processando) }
|
|
23
|
+
|
|
24
|
+
it "envia POST em /v2/#{caminho}?ref= com o JSON dos dados" do
|
|
25
|
+
recurso.emitir(ref: "pedido-42", dados: dados)
|
|
26
|
+
|
|
27
|
+
url = "#{homologacao}/v2/#{caminho}?ref=pedido-42"
|
|
28
|
+
|
|
29
|
+
expect(a_request(:post, url).with(body: JSON.generate(dados))).to have_been_made
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "devolve um Documento processando com a ref", :aggregate_failures do
|
|
33
|
+
doc = recurso.emitir(ref: "pedido-42", dados: dados)
|
|
34
|
+
|
|
35
|
+
expect(doc).to be_a(FocusNfe::Modelos::Documento)
|
|
36
|
+
expect(doc).to be_processando
|
|
37
|
+
expect(doc.ref).to eq("pedido-42")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context "quando o ambiente é produção" do
|
|
41
|
+
let(:environment) { :producao }
|
|
42
|
+
|
|
43
|
+
it "usa o host de produção" do
|
|
44
|
+
stub = stub_recurso(:post, "#{caminho}?ref=pedido-42", host: producao, status: 202, body: processando)
|
|
45
|
+
|
|
46
|
+
recurso.emitir(ref: "pedido-42", dados: dados)
|
|
47
|
+
|
|
48
|
+
expect(stub).to have_been_requested
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "propaga erro tipado da API (422)" do
|
|
53
|
+
stub_recurso(:post, "#{caminho}?ref=pedido-42", status: 422, body: '{"erros":[]}')
|
|
54
|
+
|
|
55
|
+
expect { recurso.emitir(ref: "pedido-42", dados: dados) }.to raise_error(FocusNfe::Errors::ValidationError)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "rejeita ref inválida sem requisição", :aggregate_failures do
|
|
59
|
+
expect { recurso.emitir(ref: "pedido 42", dados: dados) }.to raise_error(ArgumentError)
|
|
60
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}")).not_to have_been_made
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
RSpec.shared_examples "um recurso consultável" do |caminho|
|
|
66
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
67
|
+
|
|
68
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: :homologacao) }
|
|
69
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
70
|
+
|
|
71
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
72
|
+
|
|
73
|
+
def stub_recurso(verb, path, status: 200, body: "{}")
|
|
74
|
+
stub_request(verb, "#{homologacao}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe "#consultar" do
|
|
78
|
+
it "faz GET em /v2/#{caminho}/{ref} e devolve o Documento autorizado", :aggregate_failures do
|
|
79
|
+
stub_recurso(:get, "#{caminho}/pedido-42", body: '{"status":"autorizado","chave_nfe":"3520"}')
|
|
80
|
+
doc = recurso.consultar("pedido-42")
|
|
81
|
+
|
|
82
|
+
expect(doc).to be_autorizado
|
|
83
|
+
expect(doc.chave_nfe).to eq("3520")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "envia ?completa=1 quando completa: true" do
|
|
87
|
+
stub = stub_recurso(:get, "#{caminho}/pedido-42?completa=1", body: '{"status":"autorizado"}')
|
|
88
|
+
|
|
89
|
+
recurso.consultar("pedido-42", completa: true)
|
|
90
|
+
|
|
91
|
+
expect(stub).to have_been_requested
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "não envia completa por padrão" do
|
|
95
|
+
stub = stub_recurso(:get, "#{caminho}/pedido-42", body: '{"status":"autorizado"}')
|
|
96
|
+
|
|
97
|
+
recurso.consultar("pedido-42")
|
|
98
|
+
|
|
99
|
+
expect(stub).to have_been_requested
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "rejeita ref inválida sem requisição" do
|
|
103
|
+
expect { recurso.consultar("pedido 42") }.to raise_error(ArgumentError)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
RSpec.shared_examples "um recurso visualizável" do |caminho_previa|
|
|
109
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
110
|
+
|
|
111
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: environment) }
|
|
112
|
+
let(:environment) { :homologacao }
|
|
113
|
+
let(:dados) { { "natureza_operacao" => "Venda" } }
|
|
114
|
+
let(:pdf) { { "Content-Type" => "application/pdf" } }
|
|
115
|
+
|
|
116
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
117
|
+
def producao = "https://api.focusnfe.com.br"
|
|
118
|
+
|
|
119
|
+
def stub_previa(caminho_previa, host: homologacao, status: 200, body: "%PDF-1.4 previa")
|
|
120
|
+
stub_request(:post, "#{host}/v2/#{caminho_previa}").to_return(status: status, body: body, headers: pdf)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe "#previa" do
|
|
124
|
+
it "faz POST em /v2/#{caminho_previa} com o JSON dos dados e devolve os bytes do PDF", :aggregate_failures do
|
|
125
|
+
stub_previa(caminho_previa)
|
|
126
|
+
bytes = recurso.previa(dados: dados)
|
|
127
|
+
|
|
128
|
+
url = "#{homologacao}/v2/#{caminho_previa}"
|
|
129
|
+
|
|
130
|
+
expect(a_request(:post, url).with(body: JSON.generate(dados))).to have_been_made
|
|
131
|
+
expect(bytes).to eq("%PDF-1.4 previa")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "não valida por padrão" do
|
|
135
|
+
stub_previa(caminho_previa)
|
|
136
|
+
|
|
137
|
+
recurso.previa(dados: {})
|
|
138
|
+
|
|
139
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho_previa}")).to have_been_made
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context "quando o ambiente é produção" do
|
|
143
|
+
let(:environment) { :producao }
|
|
144
|
+
|
|
145
|
+
it "usa o host de produção" do
|
|
146
|
+
stub = stub_previa(caminho_previa, host: producao)
|
|
147
|
+
|
|
148
|
+
recurso.previa(dados: dados)
|
|
149
|
+
|
|
150
|
+
expect(stub).to have_been_requested
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
RSpec.shared_examples "um recurso corrigível" do |caminho|
|
|
157
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
158
|
+
|
|
159
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: environment) }
|
|
160
|
+
let(:environment) { :homologacao }
|
|
161
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
162
|
+
let(:correcao) { "corrigindo o endereco de entrega do destinatario" }
|
|
163
|
+
|
|
164
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
165
|
+
def producao = "https://api.focusnfe.com.br"
|
|
166
|
+
|
|
167
|
+
def stub_recurso(verb, path, host: homologacao, status: 200, body: "{}")
|
|
168
|
+
stub_request(verb, "#{host}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe "#corrigir" do
|
|
172
|
+
let(:autorizada) { '{"status":"autorizado","numero_carta_correcao":"1","caminho_xml_carta_correcao":"/cce.xml"}' }
|
|
173
|
+
|
|
174
|
+
before { stub_recurso(:post, "#{caminho}/pedido-42/carta_correcao", body: autorizada) }
|
|
175
|
+
|
|
176
|
+
it "envia POST em /v2/#{caminho}/{ref}/carta_correcao só com a correção" do
|
|
177
|
+
recurso.corrigir("pedido-42", correcao: correcao)
|
|
178
|
+
|
|
179
|
+
url = "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao"
|
|
180
|
+
|
|
181
|
+
expect(a_request(:post, url).with(body: JSON.generate(correcao: correcao))).to have_been_made
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "devolve um Documento com os dados da carta de correção", :aggregate_failures do
|
|
185
|
+
doc = recurso.corrigir("pedido-42", correcao: correcao)
|
|
186
|
+
|
|
187
|
+
expect(doc).to be_a(FocusNfe::Modelos::Documento)
|
|
188
|
+
expect(doc).to be_autorizado
|
|
189
|
+
expect(doc.numero_carta_correcao).to eq("1")
|
|
190
|
+
expect(doc.caminho_xml_carta_correcao).to eq("/cce.xml")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "inclui data_evento no corpo quando informado" do
|
|
194
|
+
recurso.corrigir("pedido-42", correcao: correcao, data_evento: "2026-06-13T10:00:00-03:00")
|
|
195
|
+
|
|
196
|
+
url = "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao"
|
|
197
|
+
corpo = JSON.generate(correcao: correcao, data_evento: "2026-06-13T10:00:00-03:00")
|
|
198
|
+
|
|
199
|
+
expect(a_request(:post, url).with(body: corpo)).to have_been_made
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "rejeita correção com menos de 15 caracteres sem requisição", :aggregate_failures do
|
|
203
|
+
expect { recurso.corrigir("pedido-42", correcao: "curta") }.to raise_error(ArgumentError)
|
|
204
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao")).not_to have_been_made
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "rejeita correção com mais de 1000 caracteres sem requisição", :aggregate_failures do
|
|
208
|
+
expect { recurso.corrigir("pedido-42", correcao: "a" * 1001) }.to raise_error(ArgumentError)
|
|
209
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao")).not_to have_been_made
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "rejeita ref inválida sem requisição" do
|
|
213
|
+
expect { recurso.corrigir("pedido 42", correcao: correcao) }.to raise_error(ArgumentError)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
context "quando o ambiente é produção" do
|
|
217
|
+
let(:environment) { :producao }
|
|
218
|
+
|
|
219
|
+
it "usa o host de produção" do
|
|
220
|
+
stub = stub_recurso(:post, "#{caminho}/pedido-42/carta_correcao", host: producao, body: autorizada)
|
|
221
|
+
|
|
222
|
+
recurso.corrigir("pedido-42", correcao: correcao)
|
|
223
|
+
|
|
224
|
+
expect(stub).to have_been_requested
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
RSpec.shared_examples "um recurso corrigível por campo" do |caminho|
|
|
231
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
232
|
+
|
|
233
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: environment) }
|
|
234
|
+
let(:environment) { :homologacao }
|
|
235
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
236
|
+
|
|
237
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
238
|
+
def producao = "https://api.focusnfe.com.br"
|
|
239
|
+
|
|
240
|
+
def stub_recurso(verb, path, host: homologacao, status: 200, body: "{}")
|
|
241
|
+
stub_request(verb, "#{host}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe "#corrigir" do
|
|
245
|
+
let(:autorizada) { '{"status":"autorizado","numero_carta_correcao":"1","caminho_xml":"/cce.xml"}' }
|
|
246
|
+
|
|
247
|
+
before { stub_recurso(:post, "#{caminho}/pedido-42/carta_correcao", body: autorizada) }
|
|
248
|
+
|
|
249
|
+
it "envia POST em /v2/#{caminho}/{ref}/carta_correcao com o campo e o valor" do
|
|
250
|
+
recurso.corrigir("pedido-42", campo_corrigido: "observacoes", valor_corrigido: "Nova observação")
|
|
251
|
+
|
|
252
|
+
url = "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao"
|
|
253
|
+
corpo = JSON.generate(campo_corrigido: "observacoes", valor_corrigido: "Nova observação")
|
|
254
|
+
|
|
255
|
+
expect(a_request(:post, url).with(body: corpo)).to have_been_made
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "devolve um Documento com os dados da carta de correção", :aggregate_failures do
|
|
259
|
+
doc = recurso.corrigir("pedido-42", campo_corrigido: "observacoes", valor_corrigido: "Nova observação")
|
|
260
|
+
|
|
261
|
+
expect(doc).to be_a(FocusNfe::Modelos::Documento)
|
|
262
|
+
expect(doc).to be_autorizado
|
|
263
|
+
expect(doc.numero_carta_correcao).to eq("1")
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "inclui grupo, número do item e campo_api no corpo quando informados" do
|
|
267
|
+
opcionais = { grupo_corrigido: "cargas", numero_item_grupo_corrigido: "1", campo_api: 0 }
|
|
268
|
+
recurso.corrigir("pedido-42", campo_corrigido: "peso", valor_corrigido: "1000", **opcionais)
|
|
269
|
+
|
|
270
|
+
url = "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao"
|
|
271
|
+
corpo = JSON.generate(campo_corrigido: "peso", valor_corrigido: "1000", **opcionais)
|
|
272
|
+
|
|
273
|
+
expect(a_request(:post, url).with(body: corpo)).to have_been_made
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "rejeita campo_corrigido vazio sem requisição", :aggregate_failures do
|
|
277
|
+
expect { recurso.corrigir("pedido-42", campo_corrigido: "", valor_corrigido: "x") }.to raise_error(ArgumentError)
|
|
278
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao")).not_to have_been_made
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "rejeita valor_corrigido vazio sem requisição", :aggregate_failures do
|
|
282
|
+
expect do
|
|
283
|
+
recurso.corrigir("pedido-42", campo_corrigido: "peso", valor_corrigido: "")
|
|
284
|
+
end.to raise_error(ArgumentError)
|
|
285
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/carta_correcao")).not_to have_been_made
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it "rejeita ref inválida sem requisição" do
|
|
289
|
+
expect do
|
|
290
|
+
recurso.corrigir("pedido 42", campo_corrigido: "peso", valor_corrigido: "1000")
|
|
291
|
+
end.to raise_error(ArgumentError)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
context "quando o ambiente é produção" do
|
|
295
|
+
let(:environment) { :producao }
|
|
296
|
+
|
|
297
|
+
it "usa o host de produção" do
|
|
298
|
+
stub = stub_recurso(:post, "#{caminho}/pedido-42/carta_correcao", host: producao, body: autorizada)
|
|
299
|
+
|
|
300
|
+
recurso.corrigir("pedido-42", campo_corrigido: "observacoes", valor_corrigido: "Nova observação")
|
|
301
|
+
|
|
302
|
+
expect(stub).to have_been_requested
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
RSpec.shared_examples "um recurso inutilizável" do |caminho|
|
|
309
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
310
|
+
|
|
311
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: environment) }
|
|
312
|
+
let(:environment) { :homologacao }
|
|
313
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
314
|
+
let(:dados) do
|
|
315
|
+
{ cnpj: "12345678000190", serie: "1", numero_inicial: "10", numero_final: "20",
|
|
316
|
+
justificativa: "erro de digitacao no sistema" }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
320
|
+
def producao = "https://api.focusnfe.com.br"
|
|
321
|
+
|
|
322
|
+
def stub_recurso(verb, path, host: homologacao, status: 200, body: "{}")
|
|
323
|
+
stub_request(verb, "#{host}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
describe "#inutilizar" do
|
|
327
|
+
let(:autorizada) { '{"status":"autorizado","protocolo_sefaz":"135200"}' }
|
|
328
|
+
|
|
329
|
+
before { stub_recurso(:post, "#{caminho}/inutilizacao", body: autorizada) }
|
|
330
|
+
|
|
331
|
+
it "envia POST em /v2/#{caminho}/inutilizacao com o JSON dos campos" do
|
|
332
|
+
recurso.inutilizar(**dados)
|
|
333
|
+
|
|
334
|
+
url = "#{homologacao}/v2/#{caminho}/inutilizacao"
|
|
335
|
+
|
|
336
|
+
expect(a_request(:post, url).with(body: JSON.generate(dados))).to have_been_made
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it "devolve uma Inutilizacao autorizada com o protocolo", :aggregate_failures do
|
|
340
|
+
inut = recurso.inutilizar(**dados)
|
|
341
|
+
|
|
342
|
+
expect(inut).to be_a(FocusNfe::Modelos::Inutilizacao)
|
|
343
|
+
expect(inut).to be_autorizado
|
|
344
|
+
expect(inut.protocolo).to eq("135200")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it "rejeita justificativa com menos de 15 caracteres sem requisição", :aggregate_failures do
|
|
348
|
+
expect { recurso.inutilizar(**dados, justificativa: "curta") }.to raise_error(ArgumentError)
|
|
349
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).not_to have_been_made
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it "rejeita numero_inicial maior que numero_final sem requisição", :aggregate_failures do
|
|
353
|
+
expect { recurso.inutilizar(**dados, numero_inicial: "20", numero_final: "10") }.to raise_error(ArgumentError)
|
|
354
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).not_to have_been_made
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
it "aceita faixa de um único número (inicial igual a final)" do
|
|
358
|
+
recurso.inutilizar(**dados, numero_inicial: "10", numero_final: "10")
|
|
359
|
+
|
|
360
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).to have_been_made
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "aceita numero_inicial e numero_final como inteiros" do
|
|
364
|
+
recurso.inutilizar(**dados, numero_inicial: 10, numero_final: 20)
|
|
365
|
+
|
|
366
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).to have_been_made
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
it "rejeita numero_inicial ausente sem requisição", :aggregate_failures do
|
|
370
|
+
expect { recurso.inutilizar(**dados, numero_inicial: nil) }.to raise_error(ArgumentError)
|
|
371
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).not_to have_been_made
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "rejeita numero_final não numérico sem requisição", :aggregate_failures do
|
|
375
|
+
expect { recurso.inutilizar(**dados, numero_final: "abc") }.to raise_error(ArgumentError)
|
|
376
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/inutilizacao")).not_to have_been_made
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
context "quando o ambiente é produção" do
|
|
380
|
+
let(:environment) { :producao }
|
|
381
|
+
|
|
382
|
+
it "usa o host de produção" do
|
|
383
|
+
stub = stub_recurso(:post, "#{caminho}/inutilizacao", host: producao, body: autorizada)
|
|
384
|
+
|
|
385
|
+
recurso.inutilizar(**dados)
|
|
386
|
+
|
|
387
|
+
expect(stub).to have_been_requested
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
describe "#consultar_inutilizacoes" do
|
|
393
|
+
it "faz GET em /v2/#{caminho}/inutilizacoes e devolve Inutilizacoes", :aggregate_failures do
|
|
394
|
+
corpo = '[{"status":"autorizado","protocolo_sefaz":"1"},{"status":"autorizado","protocolo_sefaz":"2"}]'
|
|
395
|
+
stub_recurso(:get, "#{caminho}/inutilizacoes", body: corpo)
|
|
396
|
+
|
|
397
|
+
lista = recurso.consultar_inutilizacoes
|
|
398
|
+
|
|
399
|
+
expect(lista.map(&:protocolo)).to eq(%w[1 2])
|
|
400
|
+
expect(lista).to all(be_a(FocusNfe::Modelos::Inutilizacao))
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "envia os filtros como query string" do
|
|
404
|
+
stub = stub_recurso(:get, "#{caminho}/inutilizacoes?cnpj=123&serie=1", body: "[]")
|
|
405
|
+
|
|
406
|
+
recurso.consultar_inutilizacoes(cnpj: "123", serie: "1")
|
|
407
|
+
|
|
408
|
+
expect(stub).to have_been_requested
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it "devolve lista vazia quando o corpo não é um array" do
|
|
412
|
+
stub_recurso(:get, "#{caminho}/inutilizacoes", body: "{}")
|
|
413
|
+
|
|
414
|
+
expect(recurso.consultar_inutilizacoes).to eq([])
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
RSpec.shared_examples "um recurso cancelável" do |caminho|
|
|
420
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
421
|
+
|
|
422
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: :homologacao) }
|
|
423
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
424
|
+
|
|
425
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
426
|
+
|
|
427
|
+
def stub_recurso(verb, path, status: 200, body: "{}")
|
|
428
|
+
stub_request(verb, "#{homologacao}/v2/#{path}").to_return(status: status, body: body, headers: json)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
describe "#cancelar" do
|
|
432
|
+
it "faz DELETE em /v2/#{caminho}/{ref} com a justificativa no corpo", :aggregate_failures do
|
|
433
|
+
stub_recurso(:delete, "#{caminho}/pedido-42", body: '{"status":"cancelado"}')
|
|
434
|
+
doc = recurso.cancelar("pedido-42", justificativa: "erro")
|
|
435
|
+
enviado = a_request(:delete, "#{homologacao}/v2/#{caminho}/pedido-42").with(body: '{"justificativa":"erro"}')
|
|
436
|
+
|
|
437
|
+
expect(doc).to be_cancelado
|
|
438
|
+
expect(enviado).to have_been_made
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
it "rejeita ref inválida sem requisição" do
|
|
442
|
+
expect { recurso.cancelar("pedido 42", justificativa: "x") }.to raise_error(ArgumentError)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.shared_context "com recurso conectado" do
|
|
4
|
+
subject(:recurso) { described_class.new(client.connection) }
|
|
5
|
+
|
|
6
|
+
let(:client) { FocusNfe::Client.new(token_empresa: "tok", environment: :homologacao) }
|
|
7
|
+
let(:json) { { "Content-Type" => "application/json" } }
|
|
8
|
+
|
|
9
|
+
def homologacao = "https://homologacao.focusnfe.com.br"
|
|
10
|
+
|
|
11
|
+
def stub_get(path, query: nil, status: 200, body: "{}", headers: nil)
|
|
12
|
+
stub = stub_request(:get, "#{homologacao}/v2/#{path}")
|
|
13
|
+
stub = stub.with(query: query) if query
|
|
14
|
+
stub.to_return(status: status, body: body, headers: headers || json)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stub_envio(verbo, path, query: nil, body: nil, resposta: "{}")
|
|
18
|
+
stub = stub_request(verbo, "#{homologacao}/v2/#{path}")
|
|
19
|
+
stub = stub.with(query: query) if query
|
|
20
|
+
stub = stub.with(body: body) if body
|
|
21
|
+
stub.to_return(status: 200, body: resposta, headers: json)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
RSpec.shared_examples "um recurso listável" do |caminho|
|
|
26
|
+
include_context "com recurso conectado"
|
|
27
|
+
|
|
28
|
+
describe "#listar" do
|
|
29
|
+
it "faz GET em /v2/#{caminho} e devolve uma Pagina" do
|
|
30
|
+
stub_get(caminho, body: "[]")
|
|
31
|
+
|
|
32
|
+
expect(recurso.listar).to be_a(FocusNfe::Modelos::Pagina)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "repassa os filtros como query string" do
|
|
36
|
+
stub = stub_get(caminho, query: { cnpj: "123" }, body: "[]")
|
|
37
|
+
|
|
38
|
+
recurso.listar(cnpj: "123")
|
|
39
|
+
|
|
40
|
+
expect(stub).to have_been_requested
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "expõe total e versao_maxima dos headers", :aggregate_failures do
|
|
44
|
+
headers = json.merge("X-Total-Count" => "2", "X-Max-Version" => "7")
|
|
45
|
+
stub_get(caminho, body: "[]", headers: headers)
|
|
46
|
+
|
|
47
|
+
expect(recurso.listar).to have_attributes(total: 2, versao_maxima: 7)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
RSpec.shared_examples "um recurso baixável" do |caminho|
|
|
53
|
+
include_context "com recurso conectado"
|
|
54
|
+
|
|
55
|
+
describe "#download" do
|
|
56
|
+
it "baixa o PDF seguindo o 302 para a URL pré-assinada" do
|
|
57
|
+
origem = "#{homologacao}/v2/#{caminho}/CHAVE.pdf"
|
|
58
|
+
destino = "https://arquivos.focusnfe.com.br/danfe.pdf"
|
|
59
|
+
stub_request(:get, origem).to_return(status: 302, headers: { "Location" => destino })
|
|
60
|
+
stub_request(:get, destino).to_return(status: 200, body: "%PDF")
|
|
61
|
+
|
|
62
|
+
expect(recurso.download_pdf("CHAVE")).to eq("%PDF")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "baixa o XML cru" do
|
|
66
|
+
stub_get("#{caminho}/CHAVE.xml", body: "<nfe/>", headers: { "Content-Type" => "application/xml" })
|
|
67
|
+
|
|
68
|
+
expect(recurso.download_xml("CHAVE")).to eq("<nfe/>")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "baixa o JSON cru, sem parsear (raw_body)" do
|
|
72
|
+
stub_get("#{caminho}/CHAVE.json", body: '{"a":1}')
|
|
73
|
+
|
|
74
|
+
expect(recurso.download_json("CHAVE")).to eq('{"a":1}')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "escapa o formato, sem injetar query no path" do
|
|
78
|
+
stub_request(:get, /focusnfe/).to_return(status: 200, body: "ok", headers: json)
|
|
79
|
+
|
|
80
|
+
recurso.download("CHAVE", formato: "pdf?x=1")
|
|
81
|
+
|
|
82
|
+
enviado = a_request(:get, /focusnfe/)
|
|
83
|
+
.with { |req| req.uri.query.nil? && req.uri.path.include?("CHAVE.pdf%3F") }
|
|
84
|
+
expect(enviado).to have_been_made
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
RSpec.shared_examples "um recurso localizável" do |caminho|
|
|
90
|
+
include_context "com recurso conectado"
|
|
91
|
+
|
|
92
|
+
describe "#consultar" do
|
|
93
|
+
it "faz GET em /v2/#{caminho}/{id} e devolve o corpo cru" do
|
|
94
|
+
stub_get("#{caminho}/123", body: '{"codigo":"123"}')
|
|
95
|
+
|
|
96
|
+
expect(recurso.consultar("123")).to eq("codigo" => "123")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
RSpec.shared_examples "um recurso notificável" do |caminho|
|
|
102
|
+
include_context "com recurso conectado"
|
|
103
|
+
|
|
104
|
+
describe "#reenviar_hook" do
|
|
105
|
+
it "faz POST em /v2/#{caminho}/{id}/hook" do
|
|
106
|
+
stub = stub_envio(:post, "#{caminho}/123/hook")
|
|
107
|
+
|
|
108
|
+
recurso.reenviar_hook("123")
|
|
109
|
+
|
|
110
|
+
expect(stub).to have_been_requested
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "rejeita ref inválida sem requisição" do
|
|
114
|
+
expect { recurso.reenviar_hook("pedido 42") }.to raise_error(ArgumentError)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
RSpec.shared_examples "um recurso enviável por email" do |caminho|
|
|
120
|
+
include_context "com recurso conectado"
|
|
121
|
+
|
|
122
|
+
describe "#enviar_email" do
|
|
123
|
+
it "faz POST em /v2/#{caminho}/{ref}/email com a lista de emails" do
|
|
124
|
+
stub = stub_envio(:post, "#{caminho}/pedido-42/email", body: { emails: ["a@x.com", "b@x.com"] })
|
|
125
|
+
|
|
126
|
+
recurso.enviar_email("pedido-42", emails: ["a@x.com", "b@x.com"])
|
|
127
|
+
|
|
128
|
+
expect(stub).to have_been_requested
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "devolve o corpo cru da resposta" do
|
|
132
|
+
stub_envio(:post, "#{caminho}/pedido-42/email", resposta: '{"status":"enviado"}')
|
|
133
|
+
|
|
134
|
+
expect(recurso.enviar_email("pedido-42", emails: ["a@x.com"])).to eq("status" => "enviado")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "rejeita ref inválida sem requisição" do
|
|
138
|
+
expect { recurso.enviar_email("pedido 42", emails: ["a@x.com"]) }.to raise_error(ArgumentError)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "rejeita lista de emails vazia sem requisição", :aggregate_failures do
|
|
142
|
+
expect { recurso.enviar_email("pedido-42", emails: []) }.to raise_error(ArgumentError)
|
|
143
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/email")).not_to have_been_made
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "rejeita mais de 10 emails sem requisição", :aggregate_failures do
|
|
147
|
+
emails = Array.new(11) { |i| "e#{i}@x.com" }
|
|
148
|
+
|
|
149
|
+
expect { recurso.enviar_email("pedido-42", emails: emails) }.to raise_error(ArgumentError)
|
|
150
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/email")).not_to have_been_made
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
RSpec.shared_examples "um recurso conciliável" do |caminho|
|
|
156
|
+
include_context "com recurso conectado"
|
|
157
|
+
|
|
158
|
+
let(:detalhes) { [{ "forma_pagamento" => "01", "valor_pagamento" => 1, "data_pagamento" => "2025-02-10" }] }
|
|
159
|
+
|
|
160
|
+
describe "#registrar_econf" do
|
|
161
|
+
it "faz POST em /v2/#{caminho}/{ref}/econf com detalhes_pagamento", :aggregate_failures do
|
|
162
|
+
stub_envio(:post, "#{caminho}/pedido-42/econf", body: { detalhes_pagamento: detalhes })
|
|
163
|
+
|
|
164
|
+
doc = recurso.registrar_econf("pedido-42", detalhes_pagamento: detalhes)
|
|
165
|
+
|
|
166
|
+
expect(a_request(:post, "#{homologacao}/v2/#{caminho}/pedido-42/econf")
|
|
167
|
+
.with(body: JSON.generate(detalhes_pagamento: detalhes))).to have_been_made
|
|
168
|
+
expect(doc).to be_a(FocusNfe::Modelos::Documento)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "rejeita ref inválida sem requisição" do
|
|
172
|
+
expect { recurso.registrar_econf("pedido 42", detalhes_pagamento: detalhes) }.to raise_error(ArgumentError)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe "#consultar_econf" do
|
|
177
|
+
it "faz GET em /v2/#{caminho}/{ref}/econf/{protocolo}" do
|
|
178
|
+
stub = stub_get("#{caminho}/pedido-42/econf/335250000000445")
|
|
179
|
+
|
|
180
|
+
recurso.consultar_econf("pedido-42", "335250000000445")
|
|
181
|
+
|
|
182
|
+
expect(stub).to have_been_requested
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "rejeita ref inválida sem requisição" do
|
|
186
|
+
expect { recurso.consultar_econf("pedido 42", "1") }.to raise_error(ArgumentError)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
describe "#cancelar_econf" do
|
|
191
|
+
it "faz DELETE em /v2/#{caminho}/{ref}/econf/{protocolo}" do
|
|
192
|
+
stub = stub_envio(:delete, "#{caminho}/pedido-42/econf/335250000000445")
|
|
193
|
+
|
|
194
|
+
recurso.cancelar_econf("pedido-42", "335250000000445")
|
|
195
|
+
|
|
196
|
+
expect(stub).to have_been_requested
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "rejeita ref inválida sem requisição" do
|
|
200
|
+
expect { recurso.cancelar_econf("pedido 42", "1") }.to raise_error(ArgumentError)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
RSpec.shared_examples "um recurso removível" do |caminho|
|
|
206
|
+
include_context "com recurso conectado"
|
|
207
|
+
|
|
208
|
+
describe "#excluir" do
|
|
209
|
+
it "faz DELETE em /v2/#{caminho}/{id}" do
|
|
210
|
+
stub = stub_envio(:delete, "#{caminho}/123")
|
|
211
|
+
|
|
212
|
+
recurso.excluir("123")
|
|
213
|
+
|
|
214
|
+
expect(stub).to have_been_requested
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|