Brunomm / br_nfe

Gem para emissão de notas fiscais
MIT License
60 stars 20 forks source link

Build Status Test Coverage Gem Version Code Climate

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?

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" com BrNfe::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" com BrNfe::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" com BrNfe::Service::Destinatario e BrNfe::Service::Intermediario -- Contém vários BrNfe::Service::Item através do atributo items

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:

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 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

Contribuições

Seja um contribuidor. Você pode contribuir de várias formas:

Licença


¹ Órgão Emissor: É a empresa contratada pela prefeitura com a finalidade de processar as notas fiscais. Exemplo: (Betha, Simpliss, Thema)