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. 65
      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 .certificado import CertificadoA1
from .evento import EventoCancelarNota
from .servico import Servico

26
pynfe/entidades/notafiscal.py

@ -905,3 +905,29 @@ class NotaFiscalEntregaRetirada(Entidade):
# - Telefone
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_origem = int()
icms_csosn = str()
icms_aliquota = Decimal()
icms_credito= Decimal()
# # PIS
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 SerializacaoNfse
from .validacao import Validacao
from .assinatura import AssinaturaA1
from .comunicacao import ComunicacaoSefaz

38
pynfe/processamento/assinatura.py

@ -62,4 +62,42 @@ class AssinaturaA1(Assinatura):
return xml
except Exception as 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

65
pynfe/processamento/comunicacao.py

@ -3,8 +3,9 @@ import datetime
import time
import requests
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 pynfe.entidades.certificado import CertificadoA1
@ -349,4 +350,62 @@ class ComunicacaoSefaz(Comunicacao):
except requests.exceptions.ConnectionError as e:
raise e
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, \
obter_pais_por_codigo, obter_municipio_e_codigo, \
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):
"""Classe abstrata responsavel por fornecer as funcionalidades basicas para
@ -151,7 +151,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(raiz, 'indIEDest').text = '2'
else:
# 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
# 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, 'CSOSN').text = produto_servico.icms_csosn
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, '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':
icms_item = etree.SubElement(icms, 'ICMS'+produto_servico.icms_modalidade)
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, 'vBC').text = produto_servico.pis_valor_base_calculo
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
## PISST
@ -333,7 +334,8 @@ class SerializacaoXML(Serializacao):
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, '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
## COFINSST
@ -573,6 +575,99 @@ class SerializacaoXML(Serializacao):
else:
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):
"""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
except ImportError:
raise Exception('Falhou ao importar flags')
# from geraldo.utils import 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_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_QRCODE = '100'

31
pynfe/utils/webservices.py

@ -420,4 +420,35 @@ NFE = {
'HTTPS': 'https://nfe.',
'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