Browse Source

Merge remote-tracking branch 'remotes/upstream/master' into new_mdfe

pull/79/head
leogregianin 5 years ago
parent
commit
647fbb0f62
  1. 3
      .gitignore
  2. 2
      MANIFEST.in
  3. 16
      pynfe/entidades/certificado.py
  4. 34
      pynfe/entidades/notafiscal.py
  5. 86
      pynfe/processamento/serializacao.py
  6. 26
      pynfe/utils/webservices.py
  7. 2
      requirements-nfse.txt
  8. 8
      setup.py

3
.gitignore

@ -1,6 +1,9 @@
# Pycharm
.idea/*
# VsCode
.vscode/*
# Apple OS X
.DS_Store

2
MANIFEST.in

@ -0,0 +1,2 @@
graft tests
include AUTHORS PLANEJAMENTO LICENCE *.py *.sh

16
pynfe/entidades/certificado.py

@ -35,11 +35,21 @@ class CertificadoA1(Certificado):
e retorna a string. Se caminho for True grava na pasta temporaria e retorna
o caminho dos arquivos, senao retorna o objeto. Apos o uso devem ser excluidos com o metodo excluir."""
try:
with open(self.caminho_arquivo, "rb") as cert_arquivo:
cert_conteudo = cert_arquivo.read()
except (PermissionError, FileNotFoundError) as exc:
raise Exception('Falha ao abrir arquivo do certificado digital A1. Verifique local e permissoes do arquivo.') from exc
except Exception as exc:
raise Exception('Falha ao abrir arquivo do certificado digital A1. Causa desconhecida.') from exc
# Carrega o arquivo .pfx, erro pode ocorrer se a senha estiver errada ou formato invalido.
try:
pkcs12 = crypto.load_pkcs12(open(self.caminho_arquivo, "rb").read(), senha)
except Exception as e:
raise Exception('Falha ao carregar certificado digital A1. Verifique local e senha.')
pkcs12 = crypto.load_pkcs12(cert_conteudo, senha)
except crypto.Error as exc:
raise Exception('Falha ao carregar certificado digital A1. Verifique a senha do certificado.') from exc
except Exception as exc:
raise Exception('Falha ao carregar certificado digital A1. Causa desconhecida.') from exc
if caminho:
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate())

34
pynfe/entidades/notafiscal.py

@ -145,6 +145,9 @@ class NotaFiscal(Entidade):
# - Local de entrega diferente do destinatario (Sim/Nao)
local_entrega_diferente_destinatario = False
# - Autorizados a baixar XML (lista 1 para * / ManyToManyField)
autorizados_baixar_xml = None
# - Produtos e Servicos (lista 1 para * / ManyToManyField)
produtos_e_servicos = None
@ -349,6 +352,7 @@ class NotaFiscal(Entidade):
processos_referenciados = None
def __init__(self, *args, **kwargs):
self.autorizados_baixar_xml = []
self.notas_fiscais_referenciadas = []
self.produtos_e_servicos = []
self.transporte_volumes = []
@ -362,6 +366,11 @@ class NotaFiscal(Entidade):
def __str__(self):
return ' '.join([str(self.modelo), self.serie, self.numero_nf])
def adicionar_autorizados_baixar_xml(self, **kwargs):
obj = AutorizadosBaixarXML(**kwargs)
self.autorizados_baixar_xml.append(obj)
return obj
def adicionar_nota_fiscal_referenciada(self, **kwargs):
u"""Adiciona uma instancia de Nota Fisca referenciada"""
obj = NotaFiscalReferenciada(**kwargs)
@ -589,6 +598,28 @@ class NotaFiscalProduto(Entidade):
# - Produto especifico (seleciona de lista) - NF_PRODUTOS_ESPECIFICOS
produto_especifico = str()
# Grupo de informações de Combustível
# Código de produto da ANP
cProdANP = str()
# Descrição do produto conforme ANP
descANP = str()
# Percentual de Gás derivado do Petróleo
pGLP = Decimal()
# Percentual de gás natural nacional
pGNn = Decimal()
# Percentual do gás natural importado
pGNi = Decimal()
# Valor de Partida (apenas para GLP)
vPart = Decimal()
# Sigla da UF de consumo – (OBS: Deve ser a Sigla e não o Código da UF)
UFCons = str()
# - Tributos
# - ICMS
# - Situacao tributaria (obrigatorio - seleciona de lista) - ICMS_TIPOS_TRIBUTACAO
@ -1026,3 +1057,6 @@ class NotaFiscalResponsavelTecnico(Entidade):
email = str()
fone = str()
csrt = str()
class AutorizadosBaixarXML(Entidade):
CPFCNPJ = str()

86
pynfe/processamento/serializacao.py

@ -230,6 +230,19 @@ class SerializacaoXML(Serializacao):
else:
return raiz
def _serializar_autorizados_baixar_xml(self, autorizados_baixar_xml, tag_raiz='autXML', retorna_string=True):
raiz = etree.Element(tag_raiz)
if len(so_numeros(autorizados_baixar_xml.CPFCNPJ)) == 11:
etree.SubElement(raiz, 'CPF').text = so_numeros(autorizados_baixar_xml.CPFCNPJ)
else:
etree.SubElement(raiz, 'CNPJ').text = so_numeros(autorizados_baixar_xml.CPFCNPJ)
if retorna_string:
return etree.tostring(raiz, encoding="unicode", pretty_print=True)
else:
return raiz
def _serializar_produto_servico(self, produto_servico, modelo, tag_raiz='det', retorna_string=True):
raiz = etree.Element(tag_raiz)
@ -248,7 +261,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(prod, 'CFOP').text = produto_servico.cfop
etree.SubElement(prod, 'uCom').text = produto_servico.unidade_comercial
etree.SubElement(prod, 'qCom').text = str(produto_servico.quantidade_comercial or 0)
etree.SubElement(prod, 'vUnCom').text = str('{:.4f}').format(produto_servico.valor_unitario_comercial or 0)
etree.SubElement(prod, 'vUnCom').text = str('{:.10f}').format(produto_servico.valor_unitario_comercial or 0)
""" Código Especificador da Substituição Tributária – CEST, que estabelece a sistemática de uniformização e identificação das mercadorias e bens passíveis de
sujeição aos regimes de substituição tributária e de antecipação de recolhimento do ICMS. """
#if produto_servico.cest:
@ -257,7 +270,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(prod, 'cEANTrib').text = produto_servico.ean_tributavel
etree.SubElement(prod, 'uTrib').text = produto_servico.unidade_tributavel
etree.SubElement(prod, 'qTrib').text = str(produto_servico.quantidade_tributavel)
etree.SubElement(prod, 'vUnTrib').text = '{:.4f}'.format(produto_servico.valor_unitario_tributavel or 0)
etree.SubElement(prod, 'vUnTrib').text = '{:.10f}'.format(produto_servico.valor_unitario_tributavel or 0)
# frete
if produto_servico.total_frete:
@ -286,6 +299,17 @@ class SerializacaoXML(Serializacao):
if produto_servico.numero_item:
etree.SubElement(prod, 'nItemPed').text = str(produto_servico.numero_item)
# Combustível
if produto_servico.cProdANP:
combustivel = etree.SubElement(prod, 'comb')
etree.SubElement(combustivel, 'cProdANP').text = str(produto_servico.cProdANP)
etree.SubElement(combustivel, 'descANP').text = str(produto_servico.descANP)
etree.SubElement(combustivel, 'pGLP').text = '{:.4f}'.format(produto_servico.pGLP or 0)
etree.SubElement(combustivel, 'pGNn').text = '{:.4f}'.format(produto_servico.pGNn or 0)
etree.SubElement(combustivel, 'pGNi').text = '{:.4f}'.format(produto_servico.pGNi or 0)
etree.SubElement(combustivel, 'vPart').text = '{:.2f}'.format(produto_servico.vPart or 0)
etree.SubElement(combustivel, 'UFCons').text = str(produto_servico.UFCons)
# Imposto
imposto = etree.SubElement(raiz, 'imposto')
@ -319,6 +343,10 @@ class SerializacaoXML(Serializacao):
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
elif produto_servico.icms_modalidade in ['40', '41', '50']:
icms_item = etree.SubElement(icms, 'ICMS40')
etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem)
etree.SubElement(icms_item, 'CST').text = str(produto_servico.icms_modalidade)
elif produto_servico.icms_modalidade == '51':
icms_item = etree.SubElement(icms, 'ICMS'+produto_servico.icms_modalidade)
etree.SubElement(icms_item, 'orig').text = str(produto_servico.icms_origem)
@ -362,6 +390,50 @@ class SerializacaoXML(Serializacao):
etree.SubElement(icms_item, 'vBCFCP').text = '{:.2f}'.format(produto_servico.fcp_base_calculo or 0) # Base de calculo FCP
etree.SubElement(icms_item, 'pFCP').text = '{:.2f}'.format(produto_servico.fcp_percentual or 0) # Percentual FCP
etree.SubElement(icms_item, 'vFCP').text = '{:.2f}'.format(produto_servico.fcp_valor or 0) # Valor Fundo Combate a Pobreza
# 30=Isenta / não tributada e com cobrança do ICMS por substituição tributária
elif produto_servico.icms_modalidade == '30':
etree.SubElement(icms_item, 'modBCST').text = str(produto_servico.icms_st_modalidade_determinacao_bc)
etree.SubElement(icms_item, 'pMVAST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_adicional or 0) # Percentual da margem de valor Adicionado do ICMS ST
etree.SubElement(icms_item, 'pRedBCST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_reducao_bc or 0) # APercentual da Redução de BC do ICMS ST
etree.SubElement(icms_item, 'vBCST').text = '{:.2f}'.format(produto_servico.icms_st_valor_base_calculo or 0)
etree.SubElement(icms_item, 'pICMSST').text = '{:.2f}'.format(produto_servico.icms_st_aliquota or 0)
etree.SubElement(icms_item, 'vICMSST').text = '{:.2f}'.format(produto_servico.icms_st_valor or 0)
if produto_servico.icms_desonerado > 0:
etree.SubElement(icms_item, 'vICMSDeson').text = '{:.2f}'.format(produto_servico.icms_desonerado or 0) # Valor do ICMS Desonerado
etree.SubElement(icms_item, 'motDesICMS').text = str(produto_servico.icms_motivo_desoneracao)
# 70=Com redução da BC e cobrança do ICMS por substituição tributária
elif produto_servico.icms_modalidade == '70':
etree.SubElement(icms_item, 'modBC').text = str(produto_servico.icms_modalidade_determinacao_bc)
etree.SubElement(icms_item, 'pRedBC').text = '{:.2f}'.format(produto_servico.icms_percentual_reducao_bc or 0) # Percentual da Redução de BC
etree.SubElement(icms_item, 'vBC').text = '{:.2f}'.format(produto_servico.icms_valor_base_calculo or 0) # Valor da BC do ICMS
etree.SubElement(icms_item, 'pICMS').text = '{:.2f}'.format(produto_servico.icms_aliquota or 0) # Alíquota do imposto
etree.SubElement(icms_item, 'vICMS').text = '{:.2f}'.format(produto_servico.icms_valor or 0) # Valor do ICMS
etree.SubElement(icms_item, 'modBCST').text = str(produto_servico.icms_st_modalidade_determinacao_bc)
etree.SubElement(icms_item, 'pMVAST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_adicional or 0) # Percentual da margem de valor Adicionado do ICMS ST
etree.SubElement(icms_item, 'pRedBCST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_reducao_bc or 0) # APercentual da Redução de BC do ICMS ST
etree.SubElement(icms_item, 'vBCST').text = '{:.2f}'.format(produto_servico.icms_st_valor_base_calculo or 0)
etree.SubElement(icms_item, 'pICMSST').text = '{:.2f}'.format(produto_servico.icms_st_aliquota or 0)
etree.SubElement(icms_item, 'vICMSST').text = '{:.2f}'.format(produto_servico.icms_st_valor or 0)
if produto_servico.icms_desonerado > 0:
etree.SubElement(icms_item, 'vICMSDeson').text = '{:.2f}'.format(produto_servico.icms_desonerado or 0) # Valor do ICMS Desonerado
etree.SubElement(icms_item, 'motDesICMS').text = str(produto_servico.icms_motivo_desoneracao)
# 90=Outras
elif produto_servico.icms_modalidade == '90':
etree.SubElement(icms_item, 'vBC').text = '{:.2f}'.format(produto_servico.icms_valor_base_calculo or 0) # Valor da BC do ICMS
etree.SubElement(icms_item, 'pRedBC').text = '{:.2f}'.format(produto_servico.icms_percentual_reducao_bc or 0) # Percentual da Redução de BC
etree.SubElement(icms_item, 'pICMS').text = '{:.2f}'.format(produto_servico.icms_aliquota or 0) # Alíquota do imposto
etree.SubElement(icms_item, 'vICMS').text = '{:.2f}'.format(produto_servico.icms_valor or 0) # Valor do ICMS
if (produto_servico.icms_st_valor_base_calculo > 0) and (produto_servico.icms_st_valor > 0):
etree.SubElement(icms_item, 'modBCST').text = str(produto_servico.icms_st_modalidade_determinacao_bc)
etree.SubElement(icms_item, 'pMVAST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_adicional or 0) # Percentual da margem de valor Adicionado do ICMS ST
etree.SubElement(icms_item, 'pRedBCST').text = '{:.2f}'.format(produto_servico.icms_st_percentual_reducao_bc or 0) # APercentual da Redução de BC do ICMS ST
etree.SubElement(icms_item, 'vBCST').text = '{:.2f}'.format(produto_servico.icms_st_valor_base_calculo or 0)
etree.SubElement(icms_item, 'pICMSST').text = '{:.2f}'.format(produto_servico.icms_st_aliquota or 0)
etree.SubElement(icms_item, 'vICMSST').text = '{:.2f}'.format(produto_servico.icms_st_valor or 0)
# Impostos não implementados
else:
raise NotImplementedError
@ -397,7 +469,7 @@ class SerializacaoXML(Serializacao):
pis_item = etree.SubElement(pis, 'PISQtde')
etree.SubElement(pis_item, 'CST').text = produto_servico.pis_modalidade
etree.SubElement(pis_item, 'qBCProd').text = '{:.4f}'.format(produto_servico.quantidade_comercial)
etree.SubElement(pis_item, 'vAliqProd').text = produto_servico.pis_aliquota_percentual
etree.SubElement(pis_item, 'vAliqProd').text = '{:.4f}'.format(produto_servico.pis_aliquota_percentual or 0)
etree.SubElement(pis_item, 'vPIS').text = '{:.2f}'.format(produto_servico.pis_valor_base_calculo or 0)
else:
pis_item = etree.SubElement(pis, 'PISOutr')
@ -406,7 +478,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(pis_item, 'pPIS').text = '{:.2f}'.format(produto_servico.pis_aliquota_percentual or 0)
if produto_servico.pis_modalidade is not '99':
etree.SubElement(pis_item, 'qBCProd').text = '{:.4f}'.format(produto_servico.quantidade_comercial)
etree.SubElement(pis_item, 'vAliqProd').text = produto_servico.pis_aliquota_percentual
etree.SubElement(pis_item, 'vAliqProd').text = '{:.4f}'.format(produto_servico.pis_aliquota_percentual or 0)
etree.SubElement(pis_item, 'vPIS').text = '{:.2f}'.format(produto_servico.pis_valor_base_calculo or 0)
## PISST
@ -441,7 +513,7 @@ class SerializacaoXML(Serializacao):
etree.SubElement(cofins_item, 'vBC').text = '{:.2f}'.format(produto_servico.cofins_valor_base_calculo or 0)
etree.SubElement(cofins_item, 'pCOFINS').text = '{:.2f}'.format(produto_servico.cofins_aliquota_percentual or 0)
if produto_servico.cofins_modalidade is not '99':
etree.SubElement(cofins_item, 'vAliqProd').text = '{:.2f}'.format(produto_servico.cofins_aliquota_percentual or 0)
etree.SubElement(cofins_item, 'vAliqProd').text = '{:.4f}'.format(produto_servico.cofins_aliquota_percentual or 0)
etree.SubElement(cofins_item, 'vCOFINS').text = '{:.2f}'.format(produto_servico.cofins_valor or 0)
## COFINSST
@ -571,6 +643,10 @@ class SerializacaoXML(Serializacao):
tag_raiz='entrega',
))
# Autorizados a baixar o XML
for num, item in enumerate(nota_fiscal.autorizados_baixar_xml):
raiz.append(self._serializar_autorizados_baixar_xml(item, retorna_string=False))
# Itens
for num, item in enumerate(nota_fiscal.produtos_e_servicos):
det = self._serializar_produto_servico(item, modelo=nota_fiscal.modelo, retorna_string=False)

26
pynfe/utils/webservices.py

@ -189,7 +189,7 @@ NFCE = {
'INUTILIZACAO': '',
'EVENTOS': '',
'QR': 'http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?',
'URL': 'www.nfce.fazenda.rj.gov.br/consulta'
'URL': 'www.fazenda.rj.gov.br/nfce/consulta'
},
# Os Web Services de homologação da NFC-e 4.00 são:
# https://homologacao.nfce.fazenda.sp.gov.br/ws/NFeAutorizacao4.asmx
@ -230,7 +230,10 @@ NFCE = {
'CHAVE': '',
'INUTILIZACAO': '',
'EVENTOS': '',
'QR': ''
'QR': 'sat.sef.sc.gov.br/nfce/consulta?p=',
'HTTPS': 'https://',
'HOMOLOGACAO': 'https://hom.',
'URL': 'sat.sef.sc.gov.br/nfce/consulta'
},
'RS': {
'STATUS': 'sefazrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico4.asmx',
@ -245,13 +248,16 @@ NFCE = {
'HOMOLOGACAO': 'https://nfce-homologacao.'
},
'MS': {
'STATUS': '',
'AUTORIZACAO': '',
'RECIBO': '',
'CHAVE': '',
'INUTILIZACAO': '',
'EVENTOS': '',
'QR': ''
'STATUS': 'sefaz.ms.gov.br/ws/NFeStatusServico4?wsdl',
'AUTORIZACAO': 'sefaz.ms.gov.br/ws/NFeAutorizacao4?wsdl',
'RECIBO': 'sefaz.ms.gov.br/ws/NFeRetAutorizacao4?wsdl',
'CHAVE': 'sefaz.ms.gov.br/ws/NFeConsultaProtocolo4?wsdl',
'INUTILIZACAO': 'sefaz.ms.gov.br/ws/NFeInutilizacao4?wsdl',
'EVENTOS': 'sefaz.ms.gov.br/ws/NFeRetAutorizacao4',
'QR': 'http://www.dfe.ms.gov.br/nfce/qrcode',
'URL': 'http://www.dfe.ms.gov.br/nfce/consulta',
'HTTPS': 'https://nfce.',
'HOMOLOGACAO': 'https://hom.nfce.'
},
'MT': {
'QR': 'sefaz.mt.gov.br/nfce/consultanfce?',
@ -355,7 +361,7 @@ NFE = {
'CHAVE': 'nfe.fazenda.mg.gov.br/nfe2/services/NFeConsultaProtocolo4',
'INUTILIZACAO': 'nfe.fazenda.mg.gov.br/nfe2/services/NFeInutilizacao4',
'EVENTOS': 'nfe.fazenda.mg.gov.br/nfe2/services/NFeRecepcaoEvento4',
'CADASTRO': 'nfe.fazenda.mg.gov.br/nfe2/services/cadconsultacadastro2',
'CADASTRO': 'nfe.fazenda.mg.gov.br/nfe2/services/CadConsultaCadastro4',
'HTTPS': 'https://',
'HOMOLOGACAO': 'https://h'
},

2
requirements-nfse.txt

@ -1,3 +1,3 @@
# Opcional para NFS-e
suds-jurko
pyxb=1.2.4
pyxb==1.2.4

8
setup.py

@ -7,7 +7,7 @@ setuptools.setup(
author='TadaSoftware',
author_email='tadasoftware@gmail.com',
url='https://github.com/TadaSoftware',
packages=setuptools.find_packages(),
packages=setuptools.find_packages(exclude=['tests', 'tests.*']),
package_data={
'pynfe': ['data/**/*.txt'],
},
@ -17,6 +17,12 @@ setuptools.setup(
'lxml',
'signxml',
],
extras_require={
'nfse': [
'suds-jurko',
'pyxb==1.2.4',
],
},
zip_safe=False,
python_requires='>=3.6',
)
Loading…
Cancel
Save