nfcom 0.1.2
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/.rubocop.yml +115 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +66 -0
- data/LICENSE +21 -0
- data/README.md +280 -0
- data/Rakefile +22 -0
- data/examples/.env.example +41 -0
- data/examples/emitir_nota.rb +91 -0
- data/examples/rails_initializer.rb +72 -0
- data/lib/nfcom/builder/danfe_com.rb +564 -0
- data/lib/nfcom/builder/qrcode.rb +68 -0
- data/lib/nfcom/builder/signature.rb +156 -0
- data/lib/nfcom/builder/xml_builder.rb +362 -0
- data/lib/nfcom/client.rb +106 -0
- data/lib/nfcom/configuration.rb +134 -0
- data/lib/nfcom/errors.rb +27 -0
- data/lib/nfcom/helpers/consulta.rb +28 -0
- data/lib/nfcom/models/assinante.rb +146 -0
- data/lib/nfcom/models/destinatario.rb +138 -0
- data/lib/nfcom/models/emitente.rb +105 -0
- data/lib/nfcom/models/endereco.rb +123 -0
- data/lib/nfcom/models/fatura/codigo_de_barras/formato_44.rb +52 -0
- data/lib/nfcom/models/fatura/codigo_de_barras.rb +57 -0
- data/lib/nfcom/models/fatura.rb +172 -0
- data/lib/nfcom/models/item.rb +353 -0
- data/lib/nfcom/models/nota.rb +398 -0
- data/lib/nfcom/models/total.rb +60 -0
- data/lib/nfcom/parsers/autorizacao.rb +28 -0
- data/lib/nfcom/parsers/base.rb +30 -0
- data/lib/nfcom/parsers/consulta.rb +34 -0
- data/lib/nfcom/parsers/inutilizacao.rb +23 -0
- data/lib/nfcom/parsers/status.rb +23 -0
- data/lib/nfcom/utils/certificate.rb +109 -0
- data/lib/nfcom/utils/compressor.rb +47 -0
- data/lib/nfcom/utils/helpers.rb +141 -0
- data/lib/nfcom/utils/response_decompressor.rb +47 -0
- data/lib/nfcom/utils/xml_authorized.rb +29 -0
- data/lib/nfcom/utils/xml_cleaner.rb +68 -0
- data/lib/nfcom/validators/business_rules.rb +45 -0
- data/lib/nfcom/validators/schema_validator.rb +316 -0
- data/lib/nfcom/validators/xml_validator.rb +29 -0
- data/lib/nfcom/version.rb +5 -0
- data/lib/nfcom/webservices/autorizacao.rb +36 -0
- data/lib/nfcom/webservices/base.rb +96 -0
- data/lib/nfcom/webservices/consulta.rb +59 -0
- data/lib/nfcom/webservices/inutilizacao.rb +71 -0
- data/lib/nfcom/webservices/status.rb +64 -0
- data/lib/nfcom.rb +98 -0
- data/nfcom.gemspec +42 -0
- metadata +242 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nfcom
|
|
4
|
+
module Models
|
|
5
|
+
# Representa um item (serviço) da NF-COM
|
|
6
|
+
#
|
|
7
|
+
# Cada item correspone a um serviço de comunicação/telecomunicação
|
|
8
|
+
# prestado ao cliente, como plano de internet, TV por assinatura, etc.
|
|
9
|
+
#
|
|
10
|
+
# @example Adicionar item de internet à nota
|
|
11
|
+
# nota = Nfcom::Models::Nota.new
|
|
12
|
+
#
|
|
13
|
+
# nota.add_item(
|
|
14
|
+
# codigo_servico: '0303',
|
|
15
|
+
# descricao: 'Plano Fibra 100MB',
|
|
16
|
+
# classe_consumo: '0303',
|
|
17
|
+
# cfop: '5307',
|
|
18
|
+
# unidade: 'UN',
|
|
19
|
+
# quantidade: 1,
|
|
20
|
+
# valor_unitario: 99.90
|
|
21
|
+
# )
|
|
22
|
+
#
|
|
23
|
+
# @example Item com desconto
|
|
24
|
+
# nota.add_item(
|
|
25
|
+
# codigo_servico: '0303',
|
|
26
|
+
# descricao: 'Plano Fibra 200MB',
|
|
27
|
+
# classe_consumo: '0303',
|
|
28
|
+
# cfop: '5307',
|
|
29
|
+
# valor_unitario: 149.90,
|
|
30
|
+
# valor_desconto: 20.00 # Desconto promocional
|
|
31
|
+
# )
|
|
32
|
+
# # Valor total = 149.90 - 20.00 = 129.90
|
|
33
|
+
#
|
|
34
|
+
# @example Múltiplos serviços na mesma nota
|
|
35
|
+
# # Internet
|
|
36
|
+
# nota.add_item(
|
|
37
|
+
# codigo_servico: '0303',
|
|
38
|
+
# descricao: 'Internet 100MB',
|
|
39
|
+
# classe_consumo: '0303',
|
|
40
|
+
# cfop: '5307',
|
|
41
|
+
# valor_unitario: 99.90
|
|
42
|
+
# )
|
|
43
|
+
#
|
|
44
|
+
# # TV por assinatura
|
|
45
|
+
# nota.add_item(
|
|
46
|
+
# codigo_servico: '0304',
|
|
47
|
+
# descricao: 'TV Premium',
|
|
48
|
+
# classe_consumo: '0304',
|
|
49
|
+
# cfop: '5307',
|
|
50
|
+
# valor_unitario: 79.90
|
|
51
|
+
# )
|
|
52
|
+
#
|
|
53
|
+
# Códigos de Serviço (Telecomunicações):
|
|
54
|
+
# - '0303' - Serviço de Internet
|
|
55
|
+
# - '0304' - TV por Assinatura
|
|
56
|
+
# - '0305' - Telefonia
|
|
57
|
+
#
|
|
58
|
+
# Classes de Consumo (mesmos códigos):
|
|
59
|
+
# - '0303' - Internet
|
|
60
|
+
# - '0304' - TV
|
|
61
|
+
# - '0305' - Telefonia
|
|
62
|
+
#
|
|
63
|
+
# CFOPs comuns:
|
|
64
|
+
# - '5307' - Prestação de serviço de comunicação (dentro do estado)
|
|
65
|
+
# - '6307' - Prestação de serviço de comunicação (fora do estado)
|
|
66
|
+
#
|
|
67
|
+
# Atributos obrigatórios:
|
|
68
|
+
# - codigo_servico (código do serviço de telecomunicação)
|
|
69
|
+
# - descricao (descrição do serviço/plano)
|
|
70
|
+
# - classe_consumo (classificação do consumo)
|
|
71
|
+
# - cfop (Código Fiscal de Operações)
|
|
72
|
+
# - valor_unitario (valor do serviço, maior que zero)
|
|
73
|
+
# - quantidade (quantidade de unidades, padrão: 1)
|
|
74
|
+
#
|
|
75
|
+
# Atributos opcionais:
|
|
76
|
+
# - valor_desconto (desconto aplicado, padrão: 0.00)
|
|
77
|
+
# - valor_outras_despesas (outras despesas acessórias, padrão: 0.00)
|
|
78
|
+
# - unidade (unidade de medida, padrão: 'UN')
|
|
79
|
+
# - codigo_beneficio_fiscal (código de benefício fiscal, se aplicável)
|
|
80
|
+
#
|
|
81
|
+
# Cálculo automático:
|
|
82
|
+
# - valor_total = (quantidade x valor_unitario) - valor_desconto + valor_outras_despesas
|
|
83
|
+
# - O número do item é atribuído automaticamente ao adicionar na nota
|
|
84
|
+
#
|
|
85
|
+
# Validações automáticas:
|
|
86
|
+
# - Todos os campos obrigatórios devem estar presentes
|
|
87
|
+
# - Valor unitário deve ser maior que zero
|
|
88
|
+
# - Quantidade deve ser maior que zero
|
|
89
|
+
class Item # rubocop:disable Metrics/ClassLength
|
|
90
|
+
include Utils::Helpers
|
|
91
|
+
|
|
92
|
+
attr_accessor :numero_item, :codigo_servico, :descricao,
|
|
93
|
+
:quantidade, :valor_unitario, :valor_total,
|
|
94
|
+
:valor_desconto, :valor_outras_despesas,
|
|
95
|
+
:cfop, :codigo_beneficio_fiscal
|
|
96
|
+
|
|
97
|
+
attr_reader :classe_consumo, :unidade
|
|
98
|
+
|
|
99
|
+
def classe_consumo=(value)
|
|
100
|
+
@classe_consumo = if value.is_a?(Symbol)
|
|
101
|
+
CLASSES_CONSUMO[value]
|
|
102
|
+
else
|
|
103
|
+
value.to_s
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def unidade=(value)
|
|
108
|
+
@unidade = if value.is_a?(Symbol)
|
|
109
|
+
UNIDADES_MEDIDA[value]
|
|
110
|
+
else
|
|
111
|
+
value.to_i
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Códigos de serviço principais para provedor de internet
|
|
116
|
+
CODIGOS_SERVICO = {
|
|
117
|
+
internet: '0303',
|
|
118
|
+
tv_assinatura: '0304',
|
|
119
|
+
telefonia: '0305'
|
|
120
|
+
}.freeze
|
|
121
|
+
|
|
122
|
+
CLASSES_CONSUMO = {
|
|
123
|
+
# Grupo 010 - Assinatura
|
|
124
|
+
assinatura_telefonia: '0100101',
|
|
125
|
+
assinatura_dados: '0100201',
|
|
126
|
+
assinatura_tv: '0100301',
|
|
127
|
+
assinatura_multimidia: '0100401',
|
|
128
|
+
|
|
129
|
+
# Grupo 020 - Habilitação
|
|
130
|
+
habilitacao_telefonia: '0200101',
|
|
131
|
+
habilitacao_dados: '0200201',
|
|
132
|
+
habilitacao_tv: '0200301',
|
|
133
|
+
|
|
134
|
+
# Grupo 030 - Serviço Medido
|
|
135
|
+
medido_chamadas_locais: '0300101',
|
|
136
|
+
medido_longa_distancia_nacional: '0300102',
|
|
137
|
+
medido_longa_distancia_internacional: '0300103',
|
|
138
|
+
medido_roaming_originadas: '0300104',
|
|
139
|
+
medido_roaming_recebidas: '0300105',
|
|
140
|
+
medido_adicional_chamada: '0300106',
|
|
141
|
+
medido_numeros_especiais: '0300107',
|
|
142
|
+
medido_sms: '0300108',
|
|
143
|
+
medido_mms: '0300109',
|
|
144
|
+
medido_dados: '0300201',
|
|
145
|
+
medido_pay_per_view: '0300301',
|
|
146
|
+
medido_multimidia: '0300401',
|
|
147
|
+
|
|
148
|
+
# Grupo 040 - Serviço Não Medido
|
|
149
|
+
nao_medido_telefonia: '0400101',
|
|
150
|
+
nao_medido_dados: '0400201',
|
|
151
|
+
nao_medido_tv: '0400301',
|
|
152
|
+
nao_medido_internet: '0400401',
|
|
153
|
+
nao_medido_multimidia: '0400501',
|
|
154
|
+
|
|
155
|
+
# Grupo 045 - Serviços Combinados
|
|
156
|
+
combinados_voz_dados_mensagens: '0450101',
|
|
157
|
+
|
|
158
|
+
# Grupo 050 - Serviço Pré-pago
|
|
159
|
+
prepago_cartao_telefone_fixo: '0500101',
|
|
160
|
+
prepago_recarga_fixo: '0500102',
|
|
161
|
+
prepago_recarga_movel: '0500201',
|
|
162
|
+
prepago_recarga_scm: '0500301',
|
|
163
|
+
prepago_recarga_tv: '0500401',
|
|
164
|
+
prepago_antecipacao: '0500501',
|
|
165
|
+
prepago_repasse: '0500601',
|
|
166
|
+
|
|
167
|
+
# Grupo 060 - Outros Serviços
|
|
168
|
+
outros_facilidades: '0600101',
|
|
169
|
+
outros_streaming: '0600201',
|
|
170
|
+
outros_rastreamento: '0600301',
|
|
171
|
+
outros_publicidade: '0600401',
|
|
172
|
+
outros_publicidade_radio_tv: '0600402',
|
|
173
|
+
outros_gerais: '0600501',
|
|
174
|
+
outros_valor_adicionado: '0600601',
|
|
175
|
+
|
|
176
|
+
# Grupo 070 - Cessão Meios de Rede
|
|
177
|
+
cessao_interconexao: '0700101',
|
|
178
|
+
cessao_roaming: '0700201',
|
|
179
|
+
cessao_eild: '0700301',
|
|
180
|
+
cessao_icms_proporcional: '0700401',
|
|
181
|
+
cessao_icms_consumo_proprio: '0700501',
|
|
182
|
+
cessao_icms_complementar: '0700601',
|
|
183
|
+
|
|
184
|
+
# Grupo 080 - Disponibilização de Equipamentos
|
|
185
|
+
equip_telefone: '0800101',
|
|
186
|
+
equip_identificador: '0800201',
|
|
187
|
+
equip_modem: '0800301',
|
|
188
|
+
equip_rack: '0800401',
|
|
189
|
+
equip_sala: '0800501',
|
|
190
|
+
equip_roteador: '0800601',
|
|
191
|
+
equip_servidor: '0800701',
|
|
192
|
+
equip_multiplexador: '0800801',
|
|
193
|
+
equip_decodificador: '0800901',
|
|
194
|
+
equip_outros: '0801001',
|
|
195
|
+
equip_fibra_apagada: '0801101',
|
|
196
|
+
equip_capacidade_satelital: '0801201',
|
|
197
|
+
equip_antenas: '0801301',
|
|
198
|
+
equip_dutos_postes: '0801401',
|
|
199
|
+
|
|
200
|
+
# Grupo 100 - Cobrança Própria
|
|
201
|
+
cobranca_seguros: '1000101',
|
|
202
|
+
cobranca_parcelamento: '1000201',
|
|
203
|
+
cobranca_juros: '1000301',
|
|
204
|
+
cobranca_multa_mora: '1000401',
|
|
205
|
+
cobranca_multa_fidelizacao: '1000402',
|
|
206
|
+
cobranca_meses_anteriores: '1000501',
|
|
207
|
+
cobranca_correcao_monetaria: '1000601',
|
|
208
|
+
cobranca_taxas: '1000701',
|
|
209
|
+
cobranca_adiantamento_radiodifusao: '1000801',
|
|
210
|
+
cobranca_venda_mercadorias: '1000901',
|
|
211
|
+
|
|
212
|
+
# Grupo 110 - Cobrança de Terceiros
|
|
213
|
+
terceiros_servicos: '1100101',
|
|
214
|
+
terceiros_seguros: '1100201',
|
|
215
|
+
terceiros_juros: '1100301',
|
|
216
|
+
terceiros_multa: '1100401',
|
|
217
|
+
terceiros_meses_anteriores: '1100501',
|
|
218
|
+
terceiros_correcao: '1100601',
|
|
219
|
+
terceiros_doacoes: '1100701',
|
|
220
|
+
terceiros_equipamentos: '1100801',
|
|
221
|
+
terceiros_venda_mercadorias: '1100901',
|
|
222
|
+
|
|
223
|
+
# Grupo 120 - Cobrança Centralizada
|
|
224
|
+
centralizada_item: '1200101',
|
|
225
|
+
|
|
226
|
+
# Grupo 130 - Cofaturamento
|
|
227
|
+
cofaturamento_item: '1300101',
|
|
228
|
+
|
|
229
|
+
# Grupo 590 - Deduções
|
|
230
|
+
deducao_impugnacao: '5900101',
|
|
231
|
+
deducao_ajuste: '5900201',
|
|
232
|
+
deducao_multa_interrupcao: '5900301',
|
|
233
|
+
deducao_pagamento_duplicidade: '5900401',
|
|
234
|
+
deducao_outras: '5900501'
|
|
235
|
+
}.freeze
|
|
236
|
+
|
|
237
|
+
UNIDADES_MEDIDA = {
|
|
238
|
+
minuto: 1, # Minuto
|
|
239
|
+
mb: 2, # Megabyte
|
|
240
|
+
gb: 3, # Gigabyte
|
|
241
|
+
un: 4 # Unidade/Unit (padrão para assinaturas)
|
|
242
|
+
}.freeze
|
|
243
|
+
|
|
244
|
+
def initialize(attributes = {})
|
|
245
|
+
@unidade = 4 # Padrão: UN (Unidade)
|
|
246
|
+
@quantidade = 1
|
|
247
|
+
@valor_desconto = 0.0
|
|
248
|
+
@valor_outras_despesas = 0.0
|
|
249
|
+
|
|
250
|
+
attributes.each do |key, value|
|
|
251
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
calcular_valor_total if @valor_total.nil?
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def valido?
|
|
258
|
+
erros.empty?
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def erros # rubocop:disable Metrics/MethodLength
|
|
262
|
+
errors = []
|
|
263
|
+
|
|
264
|
+
# Validações de campos obrigatórios
|
|
265
|
+
errors << 'Código de serviço é obrigatório' if codigo_servico.to_s.strip.empty?
|
|
266
|
+
errors << 'Descrição é obrigatória' if descricao.to_s.strip.empty?
|
|
267
|
+
errors << 'Classe de consumo é obrigatória' if classe_consumo.to_s.strip.empty?
|
|
268
|
+
errors << 'CFOP é obrigatório' if cfop.to_s.strip.empty?
|
|
269
|
+
|
|
270
|
+
# Validações de valores numéricos
|
|
271
|
+
errors << 'Valor unitário é obrigatório' if valor_unitario.nil?
|
|
272
|
+
errors << 'Quantidade é obrigatória' if quantidade.nil?
|
|
273
|
+
|
|
274
|
+
# Validações lógicas
|
|
275
|
+
errors << 'Valor unitário deve ser maior que zero' if valor_unitario && valor_unitario.to_f <= 0
|
|
276
|
+
errors << 'Quantidade deve ser maior que zero' if quantidade && quantidade.to_f <= 0
|
|
277
|
+
|
|
278
|
+
# Validações declarativas de formato/schema
|
|
279
|
+
campos = {}
|
|
280
|
+
|
|
281
|
+
# Campos obrigatórios - validar formato apenas se não estiverem vazios
|
|
282
|
+
unless codigo_servico.to_s.strip.empty?
|
|
283
|
+
campos[:codigo_servico] = { valor: codigo_servico, validador: :er47, nome: 'Código de serviço', max: 60 }
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
unless descricao.to_s.strip.empty?
|
|
287
|
+
campos[:descricao] = { valor: descricao, validador: :er47, nome: 'Descrição', max: 120 }
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
unless classe_consumo.to_s.strip.empty?
|
|
291
|
+
campos[:classe_consumo] = { valor: classe_consumo, validador: :er2, nome: 'Classe de consumo' }
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
campos[:cfop] = { valor: cfop, validador: :er73, nome: 'CFOP' } unless cfop.to_s.strip.empty?
|
|
295
|
+
|
|
296
|
+
# Unidade (D8 - domain: 1, 2, 3, 4)
|
|
297
|
+
if unidade && !unidade.to_s.strip.empty?
|
|
298
|
+
campos[:unidade] = { valor: unidade.to_s, validador: :d8, nome: 'Unidade de medida' }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Quantidade (ER31 - 11 posições, 0-4 decimais)
|
|
302
|
+
if quantidade&.to_f&.positive?
|
|
303
|
+
campos[:quantidade] = { valor: formatar_decimal(quantidade, 4), validador: :er31, nome: 'Quantidade' }
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Valor unitário (ER39 - 13 posições, 2-8 decimais)
|
|
307
|
+
if valor_unitario&.to_f&.positive?
|
|
308
|
+
campos[:valor_unitario] =
|
|
309
|
+
{ valor: formatar_decimal(valor_unitario, 8), validador: :er39, nome: 'Valor unitário' }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Campos opcionais - validar formato apenas se informados
|
|
313
|
+
if valor_desconto&.to_f&.positive?
|
|
314
|
+
campos[:valor_desconto] =
|
|
315
|
+
{ valor: formatar_decimal(valor_desconto, 2), validador: :er37, nome: 'Valor de desconto' }
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
if valor_outras_despesas&.to_f&.positive?
|
|
319
|
+
campos[:valor_outras_despesas] =
|
|
320
|
+
{ valor: formatar_decimal(valor_outras_despesas, 2), validador: :er37, nome: 'Valor de outras despesas' }
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
if valor_total&.to_f&.positive?
|
|
324
|
+
campos[:valor_total] = { valor: formatar_decimal(valor_total, 8), validador: :er39, nome: 'Valor total' }
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
if classe_consumo.to_s.strip.empty?
|
|
328
|
+
errors << 'Classe de consumo é obrigatória'
|
|
329
|
+
elsif !CLASSES_CONSUMO.values.include?(classe_consumo.to_s)
|
|
330
|
+
errors << "Classe de consumo inválida. Use um dos valores: #{CLASSES_CONSUMO.values.join(', ')}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Add unidade validation
|
|
334
|
+
errors << 'Unidade de medida inválida. Use: 1=Minuto, 2=MB, 3=GB, 4=UN' unless [1, 2, 3,
|
|
335
|
+
4].include?(unidade.to_i)
|
|
336
|
+
# Executar validações declarativas
|
|
337
|
+
errors.concat(Validators::SchemaValidator.validar_campos(campos))
|
|
338
|
+
|
|
339
|
+
errors
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def calcular_valor_total
|
|
343
|
+
@valor_total = (quantidade.to_f * valor_unitario.to_f) -
|
|
344
|
+
valor_desconto.to_f +
|
|
345
|
+
valor_outras_despesas.to_f
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def valor_liquido
|
|
349
|
+
valor_total.to_f
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|