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?
Manualmente
gem install br_nfe
Gemfile
gem 'br_nfe'
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.
Primeiramente é necessário ter conhecimento das operações disponíveis para cada tipo de Nota Fiscal. Vamos a elas:
Existe uma certa "padronização" das operações desenvolvidas pelos Órgãos Emissores¹. São elas:
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:
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::CancelaNfse.new(...)
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaLoteRps.new(...)
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaNfsPorRps.new(...)
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaNfse.new(...)
BrNfe::Service::ORGAO_EMISSOR::VERSAO_DO_XML::ConsultaSituacaoLoteRps.new(...)
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
.
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"
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.
É 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
Seja um contribuidor. Você pode contribuir de várias formas:
¹ Órgão Emissor: É a empresa contratada pela prefeitura com a finalidade de processar as notas fiscais. Exemplo: (Betha, Simpliss, Thema)