Browse Source

Iniciado classe de comunicação com a receita

tags/0.1.5
Danimar Ribeiro 11 years ago
parent
commit
57e502384a
  1. 24
      pytrustnfe/Certificado.py
  2. 38
      pytrustnfe/HttpClient.py
  3. 54
      pytrustnfe/servicos/Comunicacao.py
  4. 5
      pytrustnfe/servicos/comunicacao.py
  5. 13
      pytrustnfe/test/XMLs/recibo_envio_1.xml
  6. 13
      pytrustnfe/test/XMLs/recibo_envio_2.xml
  7. 21
      pytrustnfe/test/XMLs/recibo_protocolo_sucesso_1.xml
  8. 21
      pytrustnfe/test/XMLs/recibo_protocolo_sucesso_2.xml
  9. 65
      pytrustnfe/test/test_certificado.py
  10. 39
      pytrustnfe/test/test_comunicacao.py

24
pytrustnfe/Certificado.py

@ -0,0 +1,24 @@
#coding=utf-8
'''
Created on Jun 16, 2015
@author: danimar
'''
import os.path
from OpenSSL import crypto
def converte_pfx_pem(caminho, senha):
if not os.path.isfile(caminho):
raise Exception('Certificado não existe')
stream = open(caminho, 'rb').read()
try:
certificado = crypto.load_pkcs12(stream, senha)
privada = crypto.dump_privatekey(crypto.FILETYPE_PEM, certificado.get_privatekey())
certificado = crypto.dump_certificate(crypto.FILETYPE_PEM, certificado.get_certificate())
except Exception as e:
if len(e.message) == 1 and len(e.message[0])==3 and e.message[0][2] == 'mac verify failure':
raise Exception('Senha inválida')
raise
return privada, certificado

38
pytrustnfe/HttpClient.py

@ -0,0 +1,38 @@
#coding=utf-8
'''
Created on Jun 16, 2015
@author: danimar
'''
from httplib import HTTPSConnection, HTTPResponse
class HttpClient(object):
def __init__(self, url, chave_pem, certificado_pem):
self.url = url
self.chave_pem = chave_pem
self.certificado_pem = certificado_pem
def _headers(self):
return {
u'Content-type': u'application/soap+xml; charset=utf-8',
u'Accept': u'application/soap+xml; charset=utf-8'
}
def post_xml(self, post, xml):
conexao = HTTPSConnection(self.url, '443', key_file=self.chave_pem,
cert_file=self.certificado_pem)
try:
conexao.request(u'POST', post, xml, self._headers())
response = conexao.getresponse()
if response.status == '200':
return response.read()
except Exception as e:
print str(e)
finally:
conexao.close()

54
pytrustnfe/servicos/Comunicacao.py

@ -0,0 +1,54 @@
#coding=utf-8
'''
Created on Jun 14, 2015
@author: danimar
'''
from lxml import objectify
from uuid import uuid4
from pytrustnfe.HttpClient import HttpClient
from pytrustnfe.Certificado import converte_pfx_pem
import logging
from logilab.common.registry import objectify_predicate
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
class Comunicacao(object):
def __init__(self, certificado, senha):
self.certificado = certificado
self.senha = senha
def _preparar_temp_pem(self):
chave_temp = '/tmp/' + uuid4().hex
certificado_temp = '/tmp/' + uuid4().hex
chave, certificado = converte_pfx_pem(self.certificado, self.senha)
arq_temp = open(chave_temp, 'w')
arq_temp.write(chave)
arq_temp.close()
arq_temp = open(certificado_temp, 'w')
arq_temp.write(certificado)
arq_temp.close()
return chave_temp, certificado_temp
def envio_nfe(self):
chave, certificado = self._preparar_temp_pem()
c = HttpClient('cad.svrs.rs.gov.br', chave, certificado)
xml_retorno = c.post_xml('/ws/cadconsultacadastro/cadconsultacadastro2.asmx', '')
obj = objectify.fromstring(xml_retorno)
return xml_retorno, obj

5
pytrustnfe/servicos/comunicacao.py

@ -1,5 +0,0 @@
'''
Created on Jun 14, 2015
@author: danimar
'''

13
pytrustnfe/test/XMLs/recibo_envio_1.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<retEnviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140728145415</verAplic>
<cStat>103</cStat>
<xMotivo>Lote recebido com sucesso</xMotivo>
<cUF>42</cUF>
<dhRecbto>2014-08-18T10:32:32</dhRecbto>
<infRec>
<nRec>423002149000085</nRec>
<tMed>1</tMed>
</infRec>
</retEnviNFe>

13
pytrustnfe/test/XMLs/recibo_envio_2.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<retEnviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140728145415</verAplic>
<cStat>103</cStat>
<xMotivo>Lote recebido com sucesso</xMotivo>
<cUF>42</cUF>
<dhRecbto>2014-08-18T17:33:28</dhRecbto>
<infRec>
<nRec>423002149008908</nRec>
<tMed>1</tMed>
</infRec>
</retEnviNFe>

21
pytrustnfe/test/XMLs/recibo_protocolo_sucesso_1.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<retConsReciNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140729095237</verAplic>
<nRec>423002149000085</nRec>
<cStat>104</cStat>
<xMotivo>Lote processado</xMotivo>
<cUF>42</cUF>
<protNFe versao="2.00">
<infProt Id="ID342140000660576">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140729095237</verAplic>
<chNFe>42140803657739000169550020000000011000000018</chNFe>
<dhRecbto>2014-08-18T10:32:32</dhRecbto>
<nProt>342140000660576</nProt>
<digVal>ladI/iyYJbQx6QW5ihtlBMR1UUY=</digVal>
<cStat>100</cStat>
<xMotivo>Autorizado o uso da NF-e</xMotivo>
</infProt>
</protNFe>
</retConsReciNFe>

21
pytrustnfe/test/XMLs/recibo_protocolo_sucesso_2.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<retConsReciNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140729095237</verAplic>
<nRec>423002149008908</nRec>
<cStat>104</cStat>
<xMotivo>Lote processado</xMotivo>
<cUF>42</cUF>
<protNFe versao="2.00">
<infProt Id="ID342140000666829">
<tpAmb>2</tpAmb>
<verAplic>SVRS20140729095237</verAplic>
<chNFe>42140803657739000169550020000000021000000023</chNFe>
<dhRecbto>2014-08-18T17:33:28</dhRecbto>
<nProt>342140000666829</nProt>
<digVal>oQsitGdZl1I66NNzpMn8Wf6mI7c=</digVal>
<cStat>100</cStat>
<xMotivo>Autorizado o uso da NF-e</xMotivo>
</infProt>
</protNFe>
</retConsReciNFe>

65
pytrustnfe/test/test_certificado.py

@ -0,0 +1,65 @@
#coding=utf-8
'''
Created on Jun 14, 2015
@author: danimar
'''
import unittest
import os, os.path
from pytrustnfe.Certificado import converte_pfx_pem
CHAVE = '-----BEGIN PRIVATE KEY-----\n' \
'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJONRp6l1y2ojgv8\n' \
'tP3AOLW0vjWQqiPseBLM7YAxbzz5R7LYlWHC0ZJ4uIvd4Cvc6AuoNJoeuhzFcwHx\n' \
'PL0TcFuW+5up1ktUohwaJ+/zKrMODCKt0gvif302yqasMnwLh9mGZQIkLkHPOX8p\n' \
'ZQDC4dlqwOyYDi0f+bRd5C7aWx3RAgMBAAECgYADqASP+dwTLZIXifOSNikxl4D/\n' \
'Is6UhU+UZ6+a9Z6kDClSrTtGaOV4k7U/AgiEDb1STKDBEPHbtKjc63Vt2gV2teem\n' \
'ohU0Giv+gD42uuwy2DM31OfYrpR46mzOK9JrpQc78b36ealL3AWJ1gyBbbcOWbAb\n' \
'KmP742V7pcD07EEp4QJBAM/e7M8VdLgOyaQzH9KHekU6fJlI4vy1UwgRUwx3/1W6\n' \
'zlBYo1qXfc7NSVG8ZaSrJwW4rPn393u31CpXv+oc/OMCQQC1txS6nxM9+p/641HX\n' \
'CHXiWJRn0Wv7rT1FyF2dHO+OQOkCCnHCsGDMf3bacTNb7iyaPbXEDac8od5uF/3h\n' \
'aUy7AkBDPGoAeYItXqseL2Mlp6iG5+oRcp/o+YWH4IKqT84JHslI98KutL1+vKvw\n'\
'gi2mW63djeR1Xh1wqP85SvTKduHdAkAIJLlIF8Lr/yRWQQO06EsoJqIX+Pmm4L+j\n'\
'NfSECvztWhlXHxK0D+V2pKu15GbR0t2q1+Micx4wiGyIcIjPJkHrAkAvlbXGFcGT\n'\
'pk9bQ8nl7EYqlvVn1TejzTLfBhBYOse/xT/NI4Kwjkan9R+EJ1cOc9EE8gm1W3jv\n'\
'fMw/Bh2wC5kj\n'\
'-----END PRIVATE KEY-----\n'
CERTIFICADO = '-----BEGIN CERTIFICATE-----\n'\
'MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX\n'\
'MVUwUwYDVQQDHkwAewA1ADkARgAxAEUANAA2ADEALQBEAEQARQA1AC0ANABEADIA\n'\
'RgAtAEEAMAAxAEEALQA4ADMAMwAyADIAQQA5AEUAQgA4ADMAOAB9MB4XDTE1MDYx\n'\
'NTA1NDc1N1oXDTE2MDYxNDExNDc1N1owVzFVMFMGA1UEAx5MAHsANQA5AEYAMQBF\n'\
'ADQANgAxAC0ARABEAEUANQAtADQARAAyAEYALQBBADAAMQBBAC0AOAAzADMAMgAy\n'\
'AEEAOQBFAEIAOAAzADgAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAk41G\n'\
'nqXXLaiOC/y0/cA4tbS+NZCqI+x4EsztgDFvPPlHstiVYcLRkni4i93gK9zoC6g0\n'\
'mh66HMVzAfE8vRNwW5b7m6nWS1SiHBon7/Mqsw4MIq3SC+J/fTbKpqwyfAuH2YZl\n'\
'AiQuQc85fyllAMLh2WrA7JgOLR/5tF3kLtpbHdECAwEAATANBgkqhkiG9w0BAQUF\n'\
'AAOBgQArdh+RyT6VxKGsXk1zhHsgwXfToe6GpTF4W8PHI1+T0WIsNForDhvst6nm\n'\
'QtgAhuZM9rxpOJuNKc+pM29EixpAiZZiRMCSWEItNyEVdUIi+YnKBcAHd88TwO86\n'\
'd126MWQ2O8cu5W1VoDp7hYBYKOnLbYi11/StO+0rzK+oPYAvIw==\n'\
'-----END CERTIFICATE-----\n'
class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_preparar_pfx(self):
dir_pfx = os.path.join(self.caminho, 'teste.pfx')
chave, certificado = converte_pfx_pem(dir_pfx, '123456')
self.assertEqual(chave, CHAVE, 'Chave gerada inválida')
self.assertEqual(certificado, CERTIFICADO, 'Certificado gerado inválido')
def test_pfx_nao_existe(self):
self.assertRaises(Exception, converte_pfx_pem, 'file.pfx', '123456')
def test_pfx_senha_invalida(self):
dir_pfx = os.path.join(self.caminho, 'teste.pfx')
self.assertRaises(Exception, converte_pfx_pem, dir_pfx, '123')
def test_pfx_invalido(self):
dir_pfx = os.path.join(self.caminho, 'xml_assinado.xml')
self.assertRaises(Exception, converte_pfx_pem, dir_pfx, '123456')

39
pytrustnfe/test/test_comunicacao.py

@ -0,0 +1,39 @@
#coding=utf-8
'''
Created on Jun 16, 2015
@author: danimar
'''
import mock
import unittest
import os.path
from pytrustnfe.servicos.Comunicacao import Comunicacao
XML_RETORNO = '<retEnviNFe><cStat>103</cStat>' \
'<cUF>42</cUF></retEnviNFe>'
class test_comunicacao(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_envio_nfe(self):
dir_pfx = os.path.join(self.caminho, 'teste.pfx')
#dir_pfx = '/home/danimar/Desktop/INFOGER.pfx' #Hack
with mock.patch('pytrustnfe.HttpClient.HTTPSConnection') as HttpsConnection:
conn = HttpsConnection.return_value
retorno = mock.MagicMock()
type(retorno).status = mock.PropertyMock(return_value='200')
retorno.read.return_value = XML_RETORNO
conn.getresponse.return_value = retorno
com = Comunicacao(dir_pfx, '123456')
xml, objeto = com.envio_nfe()
self.assertEqual(xml, XML_RETORNO, 'Envio de NF-e com problemas - xml de retorno inválido')
self.assertEqual(objeto.cUF, 42, 'Envio de NF-e com problemas - objeto de retorno inválido')
self.assertEqual(objeto.cStat, 103, 'Envio de NF-e com problemas - objeto de retorno inválido')
Loading…
Cancel
Save