BrNfe
Gem para emissão de notas fiscais eletrônicas.
Motivação
Devido a falta de padronização dos parâmetros e a forma de envio e resposta na transmissão de Notas Fiscais de Serviços (NFS-e), esta gem vem com o objetivo de obter uma forma padronizada dessa tarefa, e assim, facilitando a vida de muitos desenvolvedores.
A Nota Fiscal eletrônica de produto está em desenvolvimento e pode ser acompanhada através da issue #4
O que essa gem faz?
- Assina digitalmente a nota fiscal eletrônica.
- Envia os parâmetros com a formatação adequada conforme documentação de cada web service desenvolvido.
- Formata em um padrão único a resposta de cada web service.
- Valida (opcionalmente) a obrigatoriedade de cada informação a ser enviada.
Instalação
Manualmente
gem install br_nfe
Gemfile
gem 'br_nfe'
#Introdução Com o objetivo de padronizar os valores, foi criado algumas classes auxiliares para montar e organizar os dados para a emissão das notas fiscais, dentre eles estão:
BrNfe::Endereco
-- Tem o objetivo de padronizar os dados de endereço para o emitente e destinatário.
####Específico para NFS-e:
BrNfe::Service::Emitente
-- Classe para instanciar o emitente da nota fiscal de serviço (NFS-e). -- Contém as validações e regras para o mesmo. -- Contém uma "Associação" comBrNfe::Endereco
BrNfe::Service::Destinatario
-- Classe para instanciar o destinatário da nota fiscal de serviço (NFS-e). -- Contém as validações e regras para o mesmo. -- Contém uma "Associação" comBrNfe::Endereco
BrNfe::Service::Intermediario
-- Classe para instanciar o intermediário na nota fiscal de serviço (NFS-e). -- Contém as validações e regras para o mesmo.
BrNfe::Service::Item
-- Item da nota fiscal de serviço; -- Alguns órgãos emissores permitem adicionar vários itens de serviço na NFS. -- Ainda, para alguns emissores é obrigatório a definição de itens de serviço, porém para outros esse item não é necessário.
BrNfe::Service::Rps
-- Classe para instanciar o RPS (Recibo Provisório de Serviço). -- Contém as validações e regras para o mesmo. -- Contém "Associação" comBrNfe::Service::Destinatario
eBrNfe::Service::Intermediario
-- Contém váriosBrNfe::Service::Item
através do atributoitems
Como citado anteriormente, essas são classes para objetos auxiliares, e serão utilizados para manter uma melhor organização da gem.
##Entendendo a organização da gem
Primeiramente é necessário ter conhecimento das operações disponíveis para cada tipo de Nota Fiscal. Vamos a elas:
Notas Fiscais de Serviço (NFS)
Existe uma certa "padronização" das operações desenvolvidas pelos Órgãos Emissores¹. São elas:
- Cancelamento de NFS (Utilizado para cancelar uma NFS)
- Consulta do Lote RPS (Utilizado para consultar um lote de RPS)
- Consulta de NFS por RPS (Utilizado para consultar uma NFS através dos dados do RPS)
- Consulta NFS (Utilizado para consultar uma ou várias NFS através do número e/ou data)
- Consulta Situação do lote RPS (Utilizado para consultar a situação de um lote de RPS para verificar se foi processado e se foi processado com sucesso ou erro)
- Recepção do lote RPS (Utilizado enviar RPS's para emissão de NFS)
Através destas operações, serão instanciados os objetos para realizar as funcionalidades para cada atividade.
Cada cidade contrata um órgão emissor para o processamento das notas (ou pode ser que a própria prefeitura desenvolva), então para realizar as operações para cada cidade, você deverá saber qual a empresa contratada para esse fim, e então instanciar o objeto de acordo com sua necessidade. A seguir segue o padrão para instanciar os objetos para cada operação:
- Para Cancelamento de NFS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::CancelaNfse.new(...)
- Para Consulta do Lote RPS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaLoteRps.new(...)
- Para Consulta de NFS por RPS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaNfsPorRps.new(...)
- Para Consulta NFS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaNfse.new(...)
- Para Consulta Situação do lote RPS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaSituacaoLoteRps.new(...)
- Para Recepção do lote RPS:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::RecepcaoLoteRps.new(...)
Para enviar os dados para processamento deve ser chamado o método request
, no qual será enviado os dados via XML para o órgão emissor correspondente.
Se desejar, antes de enviar os dados, o objeto poderá ser validado, EX:
@ws = BrNfe::Service::ORGAO_EMISSOR::V1::ConsultaLoteRps.new(...)
if @ws.valid?
@ws.request
@response = @ws.response
else
# Tratamento da validação
end
O resultado obtido na variável @response
é um objeto com os dados pertinentes e derivados de cada operação, por exemplo, se eu utilizar a operação de RecepcaoLoteRps
, então a resposta será um objeto da classe BrNfe::Service::Response::RecepcaoLoteRps
, na qual tem as informações obtidas pela resposta dessa operação. Já para a operação ConsultaLoteRps
a resposta é um objeto da classe BrNfe::Service::Response::ConsultaLoteRps
, e assim segue para cada operação. (para ver exemplos das respostas obtidas a cada operação, consulte a wiki).
Se desejar, é possível obter a resposta original (do savon) de cada órgão emissor através do método original_response
.
###Instanciando e manipulando objetos
Em todas as classes desenvolvidas é possível instanciar objetos em forma de Hash
ou Block
. Veja:
# Hash
@endereco = BrNfe::Endereco.new({
logradouro: "RUA FERNANDO MACHADO",
numero: 369,
complemento: "E",
# ...
})
# Block
@endereco = BrNfe::Endereco.new do |endereco|
endereco.logradouro = "RUA FERNANDO MACHADO"
endereco.numero = 369
endereco.complemento = "E"
# ...
})
As associações também podem ser instanciadas em forma de Hash
ou Block
, e ainda pode ser setado o objeto diretamente. Exemplo:
# Hash
@emitente = BrNfe::Service::Emitente.new({
cnpj: '11.111.111/1111-00',
...
endereco: {
logradouro: "RUA FERNANDO MACHADO",
numero: 369,
complemento: "E",
...
}
})
# Block
@emitente = BrNfe::Service::Emitente.new do |emitente|
emitente.cnpj = '11.111.111/1111-00'
...
emitente.endereco do |address|
address.logradouro = "RUA FERNANDO ...",
address.numero = 369,
address.complemento = "E",
...
end
# OU
# emitente.endereco = {
# logradouro: "RUA FERNANDO ...",
# numero: 369,
# complemento: "E",
# ...
# }
end
# Setando o objeto
@endereco = BrNfe::Endereco.new(rua: "RUA DOS PRAZERES",...)
@emitente = BrNfe::Service::Emitente.new(razao_social: 'Emitente LTDA', endereco: @endereco)
Também é possível fazer o merge
dos atributos através do método assign_attributes
, por exemplo:
@endereco = BrNfe::Endereco.new({
logradouro: "RUA 1",
numero: 100,
uf: 'SC'
})
@endereco.logradouro
# => "RUA 1"
@endereco.numero
# => 100
@endereco.uf
# => "SC"
@endereco.assign_attributes(numero: 200, uf: 'RS')
@endereco.logradouro
# => "RUA 1"
@endereco.numero
# => 200
@endereco.uf
# => "RS"
Exemplo para Recepção de um Lote RPS:
Endereço:
@endereco = BrNfe::Endereco.new({
logradouro: "RUA FERNANDO MACHADO",
numero: 369,
complemento: "E",
bairro: "CENTRO",
nome_municipio: "CHAPECÓ",
codigo_municipio: 4204202,
uf: "SC",
cep: "89665-000",
# codigo_pais: 1058, <- Default
# nome_pais: 'BRASIL', <- Default
})
Emitente:
@emitente = BrNfe::Service::Emitente.new({
cnpj: '11.111.111/1111-00',
inscricao_municipal: '66165-4',
razao_social: 'RAZÃO SOCIAL',
natureza_operacao: '1',
nome_fantasia: 'NOME FANTASIA',
telefone: '4933665577',
email: 'emitente@mail.com',
regime_especial_tributacao: '1',
codigo_regime_tributario: '1', # 1: Simples Nacional, 2: Simples Nacional(sublimite), 3: Reg. Normal
incentivo_fiscal: false,
endereco: @endereco
})
Lembrando que por padrão, sempre que for chamar @emitente.endereco
irá retornar um objeto da class BrNfe::Endereco
, mesmo que não seja setado valor algum, ex:
@emitente = BrNfe::Service::Emitente.new
@emitente.endereco
# => #<BrNfe::Endereco:0x000000022669a0 @codigo_pais="1058", ....>
Destinatário
@destinatario = BrNfe::Service::Destinatario.new({
cpf_cnpj: "111.111.111-00",
inscricao_municipal: "",
inscricao_estadual: "",
inscricao_suframa: "",
razao_social: "NOME DA PESSOA OU EMPRESA",
nome_fantasia: "",
telefone: "3365478",
email: "destinatario@mail.com",
endereco: {
logradouro: "RUA AUGUSTO VILA LOBO",
numero: 45,
complemento: "E",
bairro: "CENTRO",
nome_municipio: "FLORIANÓPOLIS",
codigo_municipio: '4205407',
uf: "SC",
cep: "89665-000",
}
})
Condição de pagamento
@condicao_pagamento = BrNfe::CondicaoPagamento.new do |cond|
cond.condicao = 'A_PRAZO' # ou 'A_VISTA'
cond.parcelas = [
{valor: 50.33, vencimento: Date.today},
{valor: '27.00', vencimento: 1.month.since}
]
end
Intermediário do serviço
@intermediario = BrNfe::Service::Intermediario.new({
cpf_cnpj: '11.111.111/0001-36',
inscricao_municipal: '3355-6',
razao_social: "INTERMEDIÁRIO DO SERVIÇO"
})
RPS
@rps = BrNfe::Service::Rps.new do |rps|
rps.destinatario = @destinatario
rps.intermediario = @intermediario
rps.condicao_pagamento = @condicao_pagamento
rps.numero = 5525
rps.serie = "SN"
rps.tipo = "1"
rps.data_emissao = DateTime.now
rps.status = "1"
rps.competencia = DateTime.now
rps.numero_substituicao = "5524"
rps.serie_substituicao = "SN"
rps.tipo_substituicao = "1"
rps.valor_servicos = 100.00
rps.valor_deducoes = "0"
rps.valor_pis = "0"
rps.valor_iss = 2.0
rps.aliquota = 0.02 # = 2%
rps.base_calculo = "100.00"
rps.item_lista_servico = "1.07"
rps.discriminacao = "1 Configuração de servidor: R$ 500.00"
rps.exigibilidade_iss = "1"
rps.codigo_municipio = "4204202"
rps.municipio_incidencia = "4204202"
end
RecepcaoLoteRps (Com o órgão emissor Betha)
@recepcao = BrNfe::Service::Betha::V1::RecepcaoLoteRps.new do |ws|
ws.emitente = @emitente
ws.lote_rps = [@rps]
ws.numero_lote_rps = 214
ws.env = :production # OU :test
ws.certificate_pkcs12_path = '/path/to/certificate.pfx'
wx.certificate_pkcs12_password = 'PASSWORD'
end
@recepcao.request
resp = @recepcao.response
#=> #<BrNfe::Service::Response::RecepcaoLoteRps:0x000016a9e28 ...>
resp.protocolo
#=> 'EX456156E'
resp.data_recebimento
#=> Fri, 23 Sep 2015 17:40:15 -0300
resp.numero_lote
#=> 214
Para demais operações consulte a WiKi.
Configurações
É possível customizar as classes auxiliares, por exemplo, se você quiser fazer alguma validação específica para o endereço, que contenha os mesmo atributos, você deve criar sua própria class
e setar na configuração da gem qual será a classe que irá representar o endereço. Exemplo:
class MeuEndereco < BrNfe::Endereco
validates :cep, length: { is: 8 }
end
emitente = BrNfe::Service::Emitente.new
emitente.endereco
#=> #<BrNfe::Endereco:0x000000016741d8 @codigo_pais="1058", @nome_pais="BRASIL">
BrNfe.endereco_class = MeuEndereco
emitente = BrNfe::Service::Emitente.new
emitente.endereco
#=> #<MeuEndereco:0x000000016741d8 @codigo_pais="1058", @nome_pais="BRASIL">
Segue as configurações possíveis
BrNfe.setup do |config|
# Classe que representa o endereço
config.endereco_class = BrNfe::Endereco
# Classe que representa os emitentes para NFS
config.emitente_service_class = BrNfe::Service::Emitente
# Classe que representa o destinatário para NFS
config.destinatario_service_class = BrNfe::Service::Destinatario
# Classe que representa o intermediário da NFS
config.intermediario_service_class = BrNfe::Service::Intermediario
# Classe que representa a condição de pagamento da NFS
config.condicao_pagamento_class = BrNfe::CondicaoPagamento
# Classe que representa o RPS da NFS
config.rps_class = BrNfe::Service::Rps
# Classe que representa o item de uma NFS
config.service_item_class = BrNfe::Service::Item
# Se você quiser exibir em log a requisição SOAP, mude para true as opções a seguir
config.client_wsdl_log = false
config.client_wsdl_pretty_print_xml = false
end
Objetivos futuros
- Emitir notas fiscais de produtos (em andamento).
- Emitir notas fiscais de serviços para todas as cidades do Brasil.
- Emitir DANFE.
- Leitura de notas fiscais de produto e serviço
Contribuições
Seja um contribuidor. Você pode contribuir de várias formas:
- Desenvolver emissão de NFS-e para outras cidades.
- Desenvolver emissão de NF-e (produtos).
- Refatorando código.
- Fornecendo Feedback construtivo (Sempre bem vindo!).
Licença
- MIT
- Copyleft 2016 Bruno Mucelini Mergen
¹ **Órgão Emissor: ** É a empresa contratada pela prefeitura com a finalidade de processar as notas fiscais. Exemplo: (Betha, Simpliss, Thema)