Browse Source

Merge branch 'master' of https://github.com/leotada/PyNFe

pull/3/head
Leonardo Tada 10 years ago
parent
commit
ccb7406f6b
  1. 1
      pynfe/entidades/__init__.py
  2. 26
      pynfe/entidades/notafiscal.py
  3. 2
      pynfe/entidades/produto.py
  4. 21
      pynfe/entidades/servico.py
  5. 1
      pynfe/processamento/__init__.py
  6. 38
      pynfe/processamento/assinatura.py
  7. 63
      pynfe/processamento/comunicacao.py
  8. 111
      pynfe/processamento/serializacao.py
  9. 1
      pynfe/utils/__init__.py
  10. 3
      pynfe/utils/flags.py
  11. 31
      pynfe/utils/webservices.py

1
pynfe/entidades/__init__.py

@ -7,4 +7,5 @@ from .lotes import LoteNotaFiscal
from .fonte_dados import _fonte_dados from .fonte_dados import _fonte_dados
from .certificado import CertificadoA1 from .certificado import CertificadoA1
from .evento import EventoCancelarNota from .evento import EventoCancelarNota
from .servico import Servico

26
pynfe/entidades/notafiscal.py

@ -905,3 +905,29 @@ class NotaFiscalEntregaRetirada(Entidade):
# - Telefone # - Telefone
endereco_telefone = str() endereco_telefone = str()
class NotaFiscalServico(Entidade):
# Empresa que implementa o webservice
autorizador = str() # betha
# id do rps
identificador = str()
# tag competencia
data_emissao = None
# Serviço executado pelo prestador
servico = None
# Emitente da NFS-e
emitente = None
# Cliente para quem a NFS-e será emitida
cliente = None
# Optante Simples Nacional
simples = int() # 1-Sim; 2-Não
# Incentivo Fiscal
incentivo = int() # 1-Sim; 2-Não
def __init__(self, *args, **kwargs):
super(NotaFiscalServico, self).__init__(*args, **kwargs)
def __str__(self):
return ' '.join([str(self.identificador)])

2
pynfe/entidades/produto.py

@ -83,6 +83,8 @@ class Produto(Entidade):
icms_modalidade = str() icms_modalidade = str()
icms_origem = int() icms_origem = int()
icms_csosn = str() icms_csosn = str()
icms_aliquota = Decimal()
icms_credito= Decimal()
# # PIS # # PIS
pis_modalidade = str() pis_modalidade = str()

21
pynfe/entidades/servico.py

@ -0,0 +1,21 @@
"""
@author: Junior Tada, Leonardo Tada
"""
from .base import Entidade
from decimal import Decimal
class Servico(Entidade):
valor_servico = Decimal()
iss_retido = Decimal()
""" http://www1.receita.fazenda.gov.br/sistemas/nfse/tabelas-de-codigos.htm
Lista com códigos dos serviços
"""
item_lista = str()
discriminacao = str()
exigibilidade = int()
codigo_municipio = str()
def __str__(self):
return self.discriminacao

1
pynfe/processamento/__init__.py

@ -1,4 +1,5 @@
from .serializacao import SerializacaoXML from .serializacao import SerializacaoXML
from .serializacao import SerializacaoNfse
from .validacao import Validacao from .validacao import Validacao
from .assinatura import AssinaturaA1 from .assinatura import AssinaturaA1
from .comunicacao import ComunicacaoSefaz from .comunicacao import ComunicacaoSefaz

38
pynfe/processamento/assinatura.py

@ -63,3 +63,41 @@ class AssinaturaA1(Assinatura):
except Exception as e: except Exception as e:
raise e raise e
def assinarNfse(self, xml, retorna_string=False):
try:
# No raiz do XML de saida
tag = 'InfDeclaracaoPrestacaoServico'; # tag que será assinada
raiz = etree.Element('Signature', xmlns='http://www.w3.org/2000/09/xmldsig#')
siginfo = etree.SubElement(raiz, 'SignedInfo')
etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
etree.SubElement(siginfo, 'SignatureMethod', Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1')
# Tenta achar a tag infNFe
ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.xpath('Rps/InfDeclaracaoPrestacaoServico')[0].attrib['Id'])
trans = etree.SubElement(ref, 'Transforms')
etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/2000/09/xmldsig#enveloped-signature')
etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
etree.SubElement(ref, 'DigestMethod', Algorithm='http://www.w3.org/2000/09/xmldsig#sha1')
etree.SubElement(ref, 'DigestValue')
etree.SubElement(raiz, 'SignatureValue')
keyinfo = etree.SubElement(raiz, 'KeyInfo')
etree.SubElement(keyinfo, 'X509Data')
rps = xml.xpath('Rps')[0]
rps.append(raiz)
# Escreve no arquivo depois de remover caracteres especiais e parse string
with open('nfse.xml', 'w') as arquivo:
arquivo.write(remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)))
subprocess.call(['xmlsec1', '--sign', '--pkcs12', self.certificado, '--pwd', self.senha, '--crypto', 'openssl', '--output', 'funfa.xml', '--id-attr:Id', tag, 'nfse.xml'])
xml = etree.parse('funfa.xml').getroot()
if retorna_string:
return etree.tostring(xml, encoding="unicode", pretty_print=False)
else:
return xml
except Exception as e:
raise e

63
pynfe/processamento/comunicacao.py

@ -3,8 +3,9 @@ import datetime
import time import time
import requests import requests
from pynfe.utils import etree, so_numeros from pynfe.utils import etree, so_numeros
from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, NAMESPACE_XSI, NAMESPACE_XSD, NAMESPACE_METODO, VERSAO_PADRAO, CODIGOS_ESTADOS
from pynfe.utils.webservices import NFCE, NFE
from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, NAMESPACE_XSI, NAMESPACE_XSD, NAMESPACE_METODO, VERSAO_PADRAO, CODIGOS_ESTADOS, \
NAMESPACE_SOAP_NFSE, NAMESPACE_BETHA
from pynfe.utils.webservices import NFCE, NFE, NFSE
from .assinatura import AssinaturaA1 from .assinatura import AssinaturaA1
from pynfe.entidades.certificado import CertificadoA1 from pynfe.entidades.certificado import CertificadoA1
@ -350,3 +351,61 @@ class ComunicacaoSefaz(Comunicacao):
raise e raise e
finally: finally:
certificadoA1.excluir() certificadoA1.excluir()
class ComunicacaoNfse(Comunicacao):
""" Classe de comunicação que segue o padrão definido para as SEFAZ dos Municípios. """
_versao = ''
def autorizacao(self, autorizador, nota):
# url do serviço
url = self._get_url(autorizador) + NFSE[autorizador.upper()]['AUTORIZACAO']
# gerar
raiz = etree.Element('GerarNfse')
# cabecalho
raiz.append(self._cabecalho_soap(autorizador))
dados = etree.SubElement(raiz, 'nfseDadosMsg')
dados.append(nota)
# xml soap
xml = self._construir_xml(raiz)
print (url)
print (etree.tostring(xml, encoding='unicode').replace(':ns0','').replace('ns0:',''))
def _cabecalho_soap(self, autorizador):
u"""Monta o XML do cabeçalho da requisição SOAP"""
if autorizador.upper() == 'BETHA':
namespace = NAMESPACE_BETHA
versao = '2.02'
raiz = etree.Element('nfseCabecMsg')
cabecalho = etree.SubElement(raiz, 'cabecalho', xmlns=namespace, versao=versao)
etree.SubElement(cabecalho, 'versaoDados').text = versao
return raiz
def _construir_xml(self, dados):
"""Mota o XML para o envio via SOAP"""
raiz = etree.Element('{%s}Envelope'%NAMESPACE_SOAP, nsmap={'soapenv': NAMESPACE_SOAP_NFSE})
etree.SubElement(raiz, '{%s}Header'%NAMESPACE_SOAP)
body = etree.SubElement(raiz, '{%s}Body'%NAMESPACE_SOAP)
body.append(dados)
return raiz
def _get_url(self, autorizador):
""" Retorna a url para comunicação com o webservice """
if self._ambiente == 1:
ambiente = 'HTTPS'
else:
ambiente = 'HOMOLOGACAO'
if autorizador.upper() in NFSE:
self.url = NFSE[autorizador.upper()][ambiente]
else:
raise Exception('Autorizador nao encontrado!')
return self.url
def _post(self, url):
pass

111
pynfe/processamento/serializacao.py

@ -6,7 +6,7 @@ from pynfe.entidades import NotaFiscal
from pynfe.utils import etree, so_numeros, obter_municipio_por_codigo, \ from pynfe.utils import etree, so_numeros, obter_municipio_por_codigo, \
obter_pais_por_codigo, obter_municipio_e_codigo, \ obter_pais_por_codigo, obter_municipio_e_codigo, \
formatar_decimal, remover_acentos, obter_uf_por_codigo, obter_codigo_por_municipio formatar_decimal, remover_acentos, obter_uf_por_codigo, obter_codigo_por_municipio
from pynfe.utils.flags import CODIGOS_ESTADOS, VERSAO_PADRAO, NAMESPACE_NFE
from pynfe.utils.flags import CODIGOS_ESTADOS, VERSAO_PADRAO, NAMESPACE_NFE, NAMESPACE_BETHA
class Serializacao(object): class Serializacao(object):
"""Classe abstrata responsavel por fornecer as funcionalidades basicas para """Classe abstrata responsavel por fornecer as funcionalidades basicas para
@ -151,7 +151,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(raiz, 'indIEDest').text = '2' etree.SubElement(raiz, 'indIEDest').text = '2'
else: else:
# Indicador da IE do destinatário: 1 – Contribuinte ICMSpagamento à vista; # Indicador da IE do destinatário: 1 – Contribuinte ICMSpagamento à vista;
etree.SubElement(raiz, 'indIEDest').text = cliente.indicador_ie
etree.SubElement(raiz, 'indIEDest').text = str(cliente.indicador_ie)
etree.SubElement(raiz, 'IE').text = cliente.inscricao_estadual etree.SubElement(raiz, 'IE').text = cliente.inscricao_estadual
# Suframa # Suframa
if cliente.inscricao_suframa: if cliente.inscricao_suframa:
@ -252,11 +252,11 @@ class SerializacaoXML(Serializacao):
etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem) etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem)
etree.SubElement(icms_item, 'CSOSN').text = produto_servico.icms_csosn etree.SubElement(icms_item, 'CSOSN').text = produto_servico.icms_csosn
elif produto_servico.icms_modalidade == '101': elif produto_servico.icms_modalidade == '101':
icms_item = etree.SubElement(icms, 'ICMS'+produto_servico.icms_modalidade)
icms_item = etree.SubElement(icms, 'ICMSSN'+produto_servico.icms_modalidade)
etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem) etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem)
etree.SubElement(icms_item, 'CSOSN').text = produto_servico.icms_csosn etree.SubElement(icms_item, 'CSOSN').text = produto_servico.icms_csosn
etree.SubElement(icms_item, 'pCredSN').text = '' # Alíquota aplicável de cálculo do crédito (Simples Nacional).
etree.SubElement(icms_item, 'vCredICMSSN').text = '' # Valor crédito do ICMS que pode ser aproveitado nos termos do art. 23 da LC 123 (Simples Nacional)
etree.SubElement(icms_item, 'pCredSN').text = str(produto_servico.icms_aliquota) # Alíquota aplicável de cálculo do crédito (Simples Nacional).
etree.SubElement(icms_item, 'vCredICMSSN').text = str(produto_servico.icms_credito) # Valor crédito do ICMS que pode ser aproveitado nos termos do art. 23 da LC 123 (Simples Nacional)
elif produto_servico.icms_modalidade == 'ST': elif produto_servico.icms_modalidade == 'ST':
icms_item = etree.SubElement(icms, 'ICMS'+produto_servico.icms_modalidade) icms_item = etree.SubElement(icms, 'ICMS'+produto_servico.icms_modalidade)
etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem) etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem)
@ -298,8 +298,9 @@ class SerializacaoXML(Serializacao):
etree.SubElement(pis_item, 'CST').text = produto_servico.pis_modalidade etree.SubElement(pis_item, 'CST').text = produto_servico.pis_modalidade
etree.SubElement(pis_item, 'vBC').text = produto_servico.pis_valor_base_calculo etree.SubElement(pis_item, 'vBC').text = produto_servico.pis_valor_base_calculo
etree.SubElement(pis_item, 'pPIS').text = produto_servico.pis_aliquota_percentual etree.SubElement(pis_item, 'pPIS').text = produto_servico.pis_aliquota_percentual
etree.SubElement(pis_item, 'qBCProd').text = produto_servico.quantidade_comercial
etree.SubElement(pis_item, 'vAliqProd').text = produto_servico.pis_aliquota_percentual
if produto_servico.pis_modalidade is not '99':
etree.SubElement(pis_item, 'qBCProd').text = produto_servico.quantidade_comercial
etree.SubElement(pis_item, 'vAliqProd').text = produto_servico.pis_aliquota_percentual
etree.SubElement(pis_item, 'vPIS').text = produto_servico.pis_valor_base_calculo etree.SubElement(pis_item, 'vPIS').text = produto_servico.pis_valor_base_calculo
## PISST ## PISST
@ -333,7 +334,8 @@ class SerializacaoXML(Serializacao):
etree.SubElement(cofins_item, 'CST').text = produto_servico.cofins_modalidade etree.SubElement(cofins_item, 'CST').text = produto_servico.cofins_modalidade
etree.SubElement(cofins_item, 'vBC').text = produto_servico.cofins_valor_base_calculo etree.SubElement(cofins_item, 'vBC').text = produto_servico.cofins_valor_base_calculo
etree.SubElement(cofins_item, 'pCOFINS').text = produto_servico.cofins_aliquota_percentual etree.SubElement(cofins_item, 'pCOFINS').text = produto_servico.cofins_aliquota_percentual
etree.SubElement(cofins_item, 'vAliqProd').text = produto_servico.cofins_aliquota_percentual
if produto_servico.cofins_modalidade is not '99':
etree.SubElement(cofins_item, 'vAliqProd').text = produto_servico.cofins_aliquota_percentual
etree.SubElement(cofins_item, 'vCOFINS').text = produto_servico.cofins_valor etree.SubElement(cofins_item, 'vCOFINS').text = produto_servico.cofins_valor
## COFINSST ## COFINSST
@ -573,6 +575,99 @@ class SerializacaoXML(Serializacao):
else: else:
return raiz return raiz
class SerializacaoNfse(Serializacao):
def exportar(self):
pass
def importar(self):
pass
def _serializar_emitente(self, emitente, tag_raiz='Prestador', retorna_string=True):
raiz = etree.Element(tag_raiz)
documento = etree.SubElement(raiz, 'CpfCnpj')
etree.SubElement(documento, 'Cnpj').text = emitente.cnpj
etree.SubElement(raiz, 'InscricaoMunicipal').text = emitente.inscricao_municipal
if retorna_string:
return etree.tostring(raiz, encoding="unicode", pretty_print=True)
else:
return raiz
def _serializar_cliente(self, cliente, tag_raiz='Tomador', retorna_string=True):
raiz = etree.Element(tag_raiz)
identificacao = etree.SubElement(raiz, 'IdentificacaoTomador')
documento = etree.SubElement(identificacao, 'CpfCnpj')
etree.SubElement(documento, cliente.tipo_documento).text = cliente.numero_documento # Apenas Cnpj ??
etree.SubElement(identificacao, 'InscricaoMunicipal').text = cliente.inscricao_municipal # obrigatório??
etree.SubElement(raiz, 'RazaoSocial').text = cliente.razao_social
endereco = etree.SubElement(raiz, 'Endereco')
etree.SubElement(endereco, 'Endereco').text = cliente.endereco_logradouro
etree.SubElement(endereco, 'Numero').text = cliente.endereco_numero
if cliente.endereco_complemento:
etree.SubElement(endereco, 'Complemento').text = cliente.endereco_complemento
etree.SubElement(endereco, 'Bairro').text = cliente.endereco_bairro
etree.SubElement(endereco, 'CodigoMunicipio').text = obter_codigo_por_municipio(
cliente.endereco_municipio, cliente.endereco_uf)
etree.SubElement(endereco, 'Uf').text = cliente.endereco_uf
etree.SubElement(endereco, 'CodigoPais').text = cliente.endereco_pais
etree.SubElement(endereco, 'Cep').text = so_numeros(cliente.endereco_cep)
contato = etree.SubElement(raiz, 'Contato')
etree.SubElement(contato, 'Telefone').text = cliente.endereco_telefone
etree.SubElement(contato, 'Email').text = cliente.email
if retorna_string:
return etree.tostring(raiz, encoding="unicode", pretty_print=True)
else:
return raiz
def _serializar_servico(self, servico, tag_raiz='Servico', retorna_string=True):
raiz = etree.Element(tag_raiz)
valores = etree.SubElement(raiz, 'Valores')
etree.SubElement(valores, 'ValorServicos').text = str('{:.2f}').format(servico.valor_servico)
etree.SubElement(raiz, 'IssRetido').text = str('{:.2f}').format(servico.iss_retido)
#etree.SubElement(raiz, 'ResponsavelRetencao').text = ''
etree.SubElement(raiz, 'ItemListaServico').text = servico.item_lista
#etree.SubElement(raiz, 'CodigoCnae').text = ''
#etree.SubElement(raiz, 'CodigoTributacaoMunicipio').text = ''
etree.SubElement(raiz, 'Discriminacao').text = servico.discriminacao
etree.SubElement(raiz, 'CodigoMunicipio').text = servico.codigo_municipio
#etree.SubElement(raiz, 'CodigoPais').text = ''
etree.SubElement(raiz, 'ExigibilidadeISS').text = str(servico.exigibilidade)
etree.SubElement(raiz, 'MunicipioIncidencia').text = servico.codigo_municipio
#etree.SubElement(raiz, 'NumeroProcesso').text = ''
if retorna_string:
return etree.tostring(raiz, encoding="unicode", pretty_print=True)
else:
return raiz
def _serializar_gerar(self, nfse, tag_raiz='GerarNfseEnvio', retorna_string=False):
if nfse.autorizador.upper() == 'BETHA':
raiz = etree.Element(tag_raiz, xmlns=NAMESPACE_BETHA)
# TODO - implementar outros sistemas autorizadores
else:
raiz = etree.Element(tag_raiz)
rps = etree.SubElement(raiz, 'Rps')
info = etree.SubElement(rps, 'InfDeclaracaoPrestacaoServico', Id=nfse.identificador)
etree.SubElement(info, 'Competencia').text = nfse.data_emissao.strftime('%Y-%m-%d')
# Servico
info.append(self._serializar_servico(nfse.servico, retorna_string=False))
# Emitente/Prestador
info.append(self._serializar_emitente(nfse.emitente, retorna_string=False))
# Cliente/Tomador
info.append(self._serializar_cliente(nfse.cliente, retorna_string=False))
etree.SubElement(info, 'OptanteSimplesNacional').text = str(nfse.simples) # 1-Sim; 2-Não
etree.SubElement(info, 'IncentivoFiscal').text = str(nfse.incentivo) # 1-Sim; 2-Não
if retorna_string:
return etree.tostring(raiz, encoding="unicode", pretty_print=True)
else:
return raiz
class SerializacaoPipes(Serializacao): class SerializacaoPipes(Serializacao):
"""Serialização utilizada pela SEFAZ-SP para a importação de notas.""" """Serialização utilizada pela SEFAZ-SP para a importação de notas."""

1
pynfe/utils/__init__.py

@ -18,7 +18,6 @@ try:
from . import flags from . import flags
except ImportError: except ImportError:
raise Exception('Falhou ao importar flags') raise Exception('Falhou ao importar flags')
# from geraldo.utils import memoize
# @memoize # @memoize

3
pynfe/utils/flags.py

@ -7,6 +7,9 @@ NAMESPACE_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
NAMESPACE_XSD = 'http://www.w3.org/2001/XMLSchema' NAMESPACE_XSD = 'http://www.w3.org/2001/XMLSchema'
NAMESPACE_METODO = 'http://www.portalfiscal.inf.br/nfe/wsdl/' NAMESPACE_METODO = 'http://www.portalfiscal.inf.br/nfe/wsdl/'
NAMESPACE_SOAP_NFSE = 'http://schemas.xmlsoap.org/soap/envelope/'
NAMESPACE_BETHA = 'http://www.betha.com.br/e-nota-contribuinte-ws'
VERSAO_PADRAO = '3.10' VERSAO_PADRAO = '3.10'
VERSAO_QRCODE = '100' VERSAO_QRCODE = '100'

31
pynfe/utils/webservices.py

@ -421,3 +421,34 @@ NFE = {
'HOMOLOGACAO': 'https://nfe-homologacao.' 'HOMOLOGACAO': 'https://nfe-homologacao.'
}, },
} }
# Nfs-e
NFSE = {
#
'BETHA': {
'STATUS':'',
'AUTORIZACAO':'GerarNfse',
'CANCELAR':'CancelarNfse',
'CONSULTA_RPS':'ConsultarNfsePorRps',
'CONSULTA_FAIXA':'ConsultarNfseFaixa',
'CONSULTA_SERVICO':'ConsultarNfseServicoPrestado',
'CONSULTA_SERVICO_TOMADO':'ConsultarNfseServicoTomado',
'SUBSTITUIR':'SubstituirNfse',
'HTTPS':'http://e-gov.betha.com.br/e-nota-contribuinte-ws/nfseWS?wsdl',
'HOMOLOGACAO':'http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?'
#'HOMOLOGACAO':'http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl'
},
#
'GINFES':{
'STATUS':'',
'AUTORIZACAO':'GerarNfse',
'CANCELAR':'CancelarNfse',
'CONSULTA_RPS':'ConsultarNfsePorRps',
'CONSULTA_FAIXA':'ConsultarNfseFaixa',
'CONSULTA_SERVICO':'ConsultarNfseServicoPrestado',
'CONSULTA_SERVICO_TOMADO':'ConsultarNfseServicoTomado',
'SUBSTITUIR':'SubstituirNfse',
'HTTPS':'https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl',
'HOMOLOGACAO':'https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl'
}
}
Loading…
Cancel
Save