diff --git a/pytrustnfe/Servidores.py b/pytrustnfe/Servidores.py
index 0b3e28d..8a340fe 100644
--- a/pytrustnfe/Servidores.py
+++ b/pytrustnfe/Servidores.py
@@ -352,8 +352,8 @@ UFBA = {
UFCE = {
NFE_AMBIENTE_PRODUCAO: {
'servidor': 'nfe.sefaz.ce.gov.br',
- WS_NFE_AUTORIZACAO: 'nfe2/services/NfeRecepcao2',
- WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetRecepcao2',
+ WS_NFE_AUTORIZACAO: 'nfe2/services/NfeAutorizacao',
+ WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetAutorizacao',
WS_NFE_INUTILIZACAO: 'nfe2/services/NfeInutilizacao2',
WS_NFE_CONSULTA: 'nfe2/services/NfeConsulta2',
WS_NFE_SITUACAO: 'nfe2/services/NfeStatusServico2',
@@ -363,8 +363,8 @@ UFCE = {
},
NFE_AMBIENTE_HOMOLOGACAO: {
'servidor': 'nfeh.sefaz.ce.gov.br',
- WS_NFE_AUTORIZACAO: 'nfe2/services/NfeRecepcao2',
- WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetRecepcao2',
+ WS_NFE_AUTORIZACAO: 'nfe2/services/NfeAutorizacao',
+ WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetAutorizacao',
WS_NFE_INUTILIZACAO: 'nfe2/services/NfeInutilizacao2',
WS_NFE_CONSULTA: 'nfe2/services/NfeConsulta2',
WS_NFE_SITUACAO: 'nfe2/services/NfeStatusServico2',
diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py
index 03adbd2..e6e67e3 100644
--- a/pytrustnfe/client.py
+++ b/pytrustnfe/client.py
@@ -45,19 +45,20 @@ class HttpClient(object):
def _headers(self, action, send_raw):
if send_raw:
return {
- 'Content-type': 'text/xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
+ 'Content-type': 'text/xml; charset=utf-8;',
+ 'SOAPAction': "http://www.portalfiscal.inf.br/nfe/wsdl/%s" % action,
'Accept': 'application/soap+xml; charset=utf-8',
}
return {
- 'Content-type': 'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
- 'Accept': 'application/soap+xml; charset=utf-8',
+ 'Content-type': 'application/soap+xml; charset=utf-8;',
+ 'SOAPAction': 'http://www.portalfiscal.inf.br/nfe/wsdl/%s' % action,
}
def post_soap(self, xml_soap, cabecalho, send_raw):
header = self._headers(cabecalho.soap_action, send_raw)
urllib3.disable_warnings(category=InsecureRequestWarning)
- res = requests.post(self.url, data=xml_soap,
+ res = requests.post(self.url, data=xml_soap.encode('utf-8'),
cert=(self.cert_path, self.key_path),
verify=False, headers=header)
return res.text
diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py
index bf87674..c903014 100644
--- a/pytrustnfe/nfe/__init__.py
+++ b/pytrustnfe/nfe/__init__.py
@@ -19,19 +19,21 @@ from pytrustnfe.exceptions import NFeValidationException
def _build_header(method, **kwargs):
action = {
- 'NfeAutorizacao': ('NfeAutorizacao', '3.10'),
- 'NfeRetAutorizacao': ('NfeRetAutorizacao', '3.10'),
- 'NfeConsultaCadastro': ('CadConsultaCadastro2', '2.00'),
- 'NfeInutilizacao': ('NfeInutilizacao2', '3.10'),
- 'RecepcaoEventoCancelamento': ('RecepcaoEvento', '1.00'),
- 'RecepcaoEventoCarta': ('RecepcaoEvento', '1.00'),
- 'NFeDistribuicaoDFe': ('NFeDistribuicaoDFe/nfeDistDFeInteresse',
- '1.00'),
- 'RecepcaoEventoManifesto': ('RecepcaoEvento', '1.00'),
+ 'NfeAutorizacao': ('NfeAutorizacao', '3.10', 'NfeAutorizacao/nfeAutorizacaoLote'),
+ 'NfeRetAutorizacao': ('NfeRetAutorizacao', '3.10', 'NfeRetAutorizacao/nfeRetAutorizacaoLote'),
+ 'NfeConsultaCadastro': ('CadConsultaCadastro2', '2.00', 'CadConsultaCadastro2/consultaCadastro2'),
+ 'NfeInutilizacao': ('NfeInutilizacao2', '3.10', 'NfeInutilizacao2/nfeInutilizacaoNF2'),
+ 'RecepcaoEventoCancelamento': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'),
+ 'RecepcaoEventoCarta': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'),
+ 'NFeDistribuicaoDFe': ('NFeDistribuicaoDFe/nfeDistDFeInteresse', '1.00', 'NFeDistribuicaoDFe/nfeDistDFeInteresse'),
+ 'RecepcaoEventoManifesto': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'),
+ }
+ vals = {
+ 'estado': kwargs['estado'],
+ 'method': action[method][0],
+ 'soap_action': action[method][2],
+ 'versao': action[method][1]
}
- vals = {'estado': kwargs['estado'],
- 'soap_action': action[method][0],
- 'versao': action[method][1]}
return CabecalhoSoap(**vals)
diff --git a/pytrustnfe/nfe/comunicacao.py b/pytrustnfe/nfe/comunicacao.py
index df6ba53..34c550e 100644
--- a/pytrustnfe/nfe/comunicacao.py
+++ b/pytrustnfe/nfe/comunicacao.py
@@ -12,9 +12,9 @@ from ..xml import sanitize_response
def _soap_xml(body, cabecalho):
xml = ''
xml += ''
- xml += ''
+ xml += ''
xml += '' + cabecalho.estado + '' + cabecalho.versao + ''
- xml += ''
+ xml += ''
xml += body
xml += ''
return xml.rstrip('\n')
diff --git a/pytrustnfe/nfse/bh/__init__.py b/pytrustnfe/nfse/bh/__init__.py
new file mode 100644
index 0000000..4c9fd3f
--- /dev/null
+++ b/pytrustnfe/nfse/bh/__init__.py
@@ -0,0 +1,78 @@
+# © 2018 Danimar Ribeiro, Trustcode
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import os
+
+from requests import Session
+from zeep import Client
+from zeep.transports import Transport
+
+from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
+from pytrustnfe.xml import render_xml, sanitize_response
+from pytrustnfe.nfe.assinatura import Assinatura
+
+
+def _render(certificado, method, **kwargs):
+ path = os.path.join(os.path.dirname(__file__), 'templates')
+ xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
+
+ reference = ''
+ if method == 'GerarNfse':
+ reference = 'r%s' % kwargs['rps']['numero']
+ elif method == 'CancelarNfse':
+ reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse']
+
+ signer = Assinatura(certificado.pfx, certificado.password)
+ xml_send = signer.assina_xml(xml_send, reference)
+ return xml_send.encode('utf-8')
+
+
+def _send(certificado, method, **kwargs):
+ base_url = ''
+ if kwargs['ambiente'] == 'producao':
+ base_url = 'https://bhissdigital.pbh.gov.br/bhiss-ws/nfse?wsdl'
+ else:
+ base_url = 'https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl'
+
+ xml_send = kwargs["xml"].decode('utf-8')
+ xml_cabecalho = ''
+
+ cert, key = extract_cert_and_key_from_pfx(
+ certificado.pfx, certificado.password)
+ cert, key = save_cert_key(cert, key)
+
+ session = Session()
+ session.cert = (cert, key)
+ session.verify = False
+ transport = Transport(session=session)
+
+ client = Client(base_url, transport=transport)
+
+ response = client.service[method](xml_cabecalho, xml_send)
+
+ response, obj = sanitize_response(response)
+ return {
+ 'sent_xml': str(xml_send),
+ 'received_xml': str(response),
+ 'object': obj
+ }
+
+
+def xml_gerar_nfse(certificado, **kwargs):
+ return _render(certificado, 'GerarNfse', **kwargs)
+
+
+def gerar_nfse(certificado, **kwargs):
+ if "xml" not in kwargs:
+ kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
+ return _send(certificado, 'GerarNfse', **kwargs)
+
+
+def xml_cancelar_nfse(certificado, **kwargs):
+ return _render(certificado, 'CancelarNfse', **kwargs)
+
+
+def cancelar_nfse(certificado, **kwargs):
+ if "xml" not in kwargs:
+ kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
+ return _send(certificado, 'CancelarNfse', **kwargs)
diff --git a/pytrustnfe/nfse/bh/templates/CancelarNfse.xml b/pytrustnfe/nfse/bh/templates/CancelarNfse.xml
new file mode 100644
index 0000000..137897e
--- /dev/null
+++ b/pytrustnfe/nfse/bh/templates/CancelarNfse.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ {{ cancelamento.numero_nfse }}
+ {{ cancelamento.cnpj_prestador }}
+ {{ cancelamento.inscricao_municipal }}
+ {{ cancelamento.cidade }}
+
+ 1
+
+
+
diff --git a/pytrustnfe/nfse/bh/templates/GerarNfse.xml b/pytrustnfe/nfse/bh/templates/GerarNfse.xml
new file mode 100644
index 0000000..c6ead33
--- /dev/null
+++ b/pytrustnfe/nfse/bh/templates/GerarNfse.xml
@@ -0,0 +1,11 @@
+
+
+ {{ nfse.numero_lote }}
+ {{ nfse.cnpj_prestador }}
+ {{ nfse.inscricao_municipal }}
+ 1
+
+ {% include 'Rps.xml' %}
+
+
+
diff --git a/pytrustnfe/nfse/bh/templates/Rps.xml b/pytrustnfe/nfse/bh/templates/Rps.xml
new file mode 100644
index 0000000..7b883af
--- /dev/null
+++ b/pytrustnfe/nfse/bh/templates/Rps.xml
@@ -0,0 +1,91 @@
+
+
+
+ {{ rps.numero }}
+ {{ rps.serie }}
+ {{ rps.tipo_rps }}
+
+ {{ rps.data_emissao }}
+ {{ rps.natureza_operacao }}
+ {{ rps.regime_tributacao }}
+ {{ rps.optante_simples }}
+ {{ rps.incentivador_cultural }}
+ {{ rps.status }}
+
+ {{ rps.numero_substituido }}
+ {{ rps.serie_substituido }}
+ {{ rps.tipo_substituido }}
+
+
+
+ {{ rps.valor_servico }}
+ {{ rps.valor_deducao }}
+ {{ rps.valor_pis }}
+ {{ rps.valor_cofins }}
+ {{ rps.valor_inss }}
+ {{ rps.valor_ir }}
+ {{ rps.valor_csll }}
+ {{ rps.iss_retido }}
+ {{ rps.valor_iss }}
+ {{ rps.valor_iss_retido }}
+ {{ rps.outras_retencoes }}
+ {{ rps.base_calculo }}
+ {{ rps.aliquota_issqn }}
+ {{ rps.valor_liquido_nfse }}
+ {{ rps.desconto_incondicionado }}
+ {{ rps.desconto_condicionado }}
+
+ {{ rps.codigo_servico }}
+ {{ rps.cnae_servico }}
+ {{ rps.codigo_tributacao_municipio }}
+ {{ rps.descricao }}
+ {{ rps.codigo_municipio }}
+
+
+ {{ rps.prestador.cnpj }}
+ {{ rps.prestador.inscricao_municipal }}
+
+
+
+
+ {% if rps.tomador.cnpj_cpf|length == 14 %}
+ {{ rps.tomador.cnpj_cpf }}
+ {% endif %}
+ {% if rps.tomador.cnpj_cpf|length == 11 %}
+ {{ rps.tomador.cnpj_cpf }}
+ {% endif %}
+
+ {{ rps.tomador.inscricao_municipal }}
+
+ {{ rps.tomador.razao_social }}
+
+ {{ rps.tomador.logradouro }}
+ {{ rps.tomador.numero }}
+ {{ rps.tomador.complemento }}
+ {{ rps.tomador.bairro }}
+ {{ rps.tomador.cidade }}
+ {{ rps.tomador.uf }}
+ {{ rps.tomador.cep }}
+
+
+ {{ rps.tomador.telefone }}
+ {{ rps.tomador.email }}
+
+
+ {% if rps.intermediario is defined -%}
+
+ {{ rps.intermediario.razao_social }}
+
+ {{ rps.intermediario.cnpj }}
+
+ {{ rps.intermediario.inscricao_municipal }}
+
+ {% endif %}
+ {% if rps.construcao_civil is defined -%}
+
+ {{ rps.construcao_civil.codigo_obra }}
+ {{ rps.construcao_civil.art }}
+
+ {% endif %}
+
+
diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py
index c494496..b06fb6e 100644
--- a/pytrustnfe/utils.py
+++ b/pytrustnfe/utils.py
@@ -13,6 +13,7 @@ class CabecalhoSoap(object):
def __init__(self, **kwargs):
self.versao = kwargs.pop('versao', '')
self.estado = kwargs.pop('estado', '')
+ self.method = kwargs.pop('method', '')
self.soap_action = kwargs.pop('soap_action', '')