7 changed files with 244 additions and 2 deletions
-
2pytrustnfe/nfe/templates/NfeAutorizacao.xml
-
82pytrustnfe/nfse/mga/__init__.py
-
44pytrustnfe/nfse/mga/assinatura.py
-
13pytrustnfe/nfse/mga/templates/CancelarNfse.xml
-
11pytrustnfe/nfse/mga/templates/GerarNfse.xml
-
91pytrustnfe/nfse/mga/templates/Rps.xml
-
3setup.py
@ -0,0 +1,82 @@ |
|||
# © 2018 Danimar Ribeiro, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import os |
|||
from lxml import etree |
|||
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.nfse.mga.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 = 'rps:%s' % kwargs['rps']['numero'] |
|||
ref_lote = 'lote%s' % kwargs['rps']['numero_lote'] |
|||
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) |
|||
xml_send = signer.assina_xml(etree.fromstring(xml_send), ref_lote) |
|||
return xml_send.encode('utf-8') |
|||
|
|||
|
|||
def _send(certificado, method, **kwargs): |
|||
base_url = '' |
|||
if kwargs['ambiente'] == 'producao': |
|||
base_url = 'https://isse.maringa.gov.br/ws/?wsdl' |
|||
else: |
|||
base_url = 'https://isseteste.maringa.gov.br/ws/?wsdl' |
|||
|
|||
xml_send = kwargs["xml"].decode('utf-8') |
|||
xml_cabecalho = '<?xml version="1.0" encoding="UTF-8"?>\ |
|||
<cabecalho xmlns="http://www.abrasf.org.br/nfse.xsd" versao="1.00">\ |
|||
<versaoDados>1.00</versaoDados></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.encode('utf-8')) |
|||
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) |
|||
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2016 Danimar Ribeiro, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import signxml |
|||
from lxml import etree |
|||
from pytrustnfe.certificado import extract_cert_and_key_from_pfx |
|||
from signxml import XMLSigner |
|||
|
|||
|
|||
class Assinatura(object): |
|||
|
|||
def __init__(self, arquivo, senha): |
|||
self.arquivo = arquivo |
|||
self.senha = senha |
|||
|
|||
def assina_xml(self, xml_element, reference): |
|||
cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha) |
|||
|
|||
for element in xml_element.iter("*"): |
|||
if element.text is not None and not element.text.strip(): |
|||
element.text = None |
|||
|
|||
signer = XMLSigner( |
|||
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1", |
|||
digest_algorithm='sha1', |
|||
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') |
|||
|
|||
ns = {} |
|||
ns[None] = signer.namespaces['ds'] |
|||
signer.namespaces = ns |
|||
|
|||
ref_uri = ('#%s' % reference) if reference else None |
|||
signed_root = signer.sign( |
|||
xml_element, key=key.encode(), cert=cert.encode(), |
|||
reference_uri=ref_uri) |
|||
if reference: |
|||
element_signed = signed_root.find(".//*[@Id='%s']" % reference) |
|||
signature = signed_root.find(".//*[@URI='#%s']" % reference).getparent().getparent() |
|||
|
|||
if element_signed is not None and signature is not None: |
|||
parent = element_signed.getparent() |
|||
parent.append(signature) |
|||
return etree.tostring(signed_root, encoding=str) |
|||
@ -0,0 +1,13 @@ |
|||
<CancelarNfseEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd"> |
|||
<Pedido xmlns="http://www.abrasf.org.br/nfse.xsd"> |
|||
<InfPedidoCancelamento Id="pedidoCancelamento_{{ cancelamento.numero_nfse }}"> |
|||
<IdentificacaoNfse> |
|||
<Numero>{{ cancelamento.numero_nfse }}</Numero> |
|||
<Cnpj>{{ cancelamento.cnpj_prestador }}</Cnpj> |
|||
<InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipal> |
|||
<CodigoMunicipio>{{ cancelamento.cidade }}</CodigoMunicipio> |
|||
</IdentificacaoNfse> |
|||
<CodigoCancelamento>1</CodigoCancelamento> |
|||
</InfPedidoCancelamento> |
|||
</Pedido> |
|||
</CancelarNfseEnvio> |
|||
@ -0,0 +1,11 @@ |
|||
<GerarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd"> |
|||
<LoteRps Id="lote{{ rps.numero_lote }}" versao="1.00"> |
|||
<NumeroLote>{{ rps.numero_lote }}</NumeroLote> |
|||
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj> |
|||
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal> |
|||
<QuantidadeRps>1</QuantidadeRps> |
|||
<ListaRps xmlns="http://www.abrasf.org.br/nfse.xsd"> |
|||
{% include 'Rps.xml' %} |
|||
</ListaRps> |
|||
</LoteRps> |
|||
</GerarNfseEnvio> |
|||
@ -0,0 +1,91 @@ |
|||
<Rps> |
|||
<InfRps xmlns="http://www.abrasf.org.br/nfse.xsd" Id="rps:{{ rps.numero }}"> |
|||
<IdentificacaoRps> |
|||
<Numero>{{ rps.numero }}</Numero> |
|||
<Serie>{{ rps.serie }}</Serie> |
|||
<Tipo>{{ rps.tipo_rps }}</Tipo> |
|||
</IdentificacaoRps> |
|||
<DataEmissao>{{ rps.data_emissao }}</DataEmissao> |
|||
<NaturezaOperacao>{{ rps.natureza_operacao }}</NaturezaOperacao> |
|||
<RegimeEspecialTributacao>{{ rps.regime_tributacao }}</RegimeEspecialTributacao> |
|||
<OptanteSimplesNacional>{{ rps.optante_simples }}</OptanteSimplesNacional> |
|||
<IncentivadorCultural>{{ rps.incentivador_cultural }}</IncentivadorCultural> |
|||
<Status>{{ rps.status }}</Status> |
|||
<RpsSubstituido> |
|||
<Numero>{{ rps.numero_substituido }}</Numero> |
|||
<Serie>{{ rps.serie_substituido }}</Serie> |
|||
<Tipo>{{ rps.tipo_substituido }}</Tipo> |
|||
</RpsSubstituido> |
|||
<Servico> |
|||
<Valores> |
|||
<ValorServicos>{{ rps.valor_servico }}</ValorServicos> |
|||
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes> |
|||
<ValorPis>{{ rps.valor_pis }}</ValorPis> |
|||
<ValorCofins>{{ rps.valor_cofins }}</ValorCofins> |
|||
<ValorInss>{{ rps.valor_inss }}</ValorInss> |
|||
<ValorIr>{{ rps.valor_ir }}</ValorIr> |
|||
<ValorCsll>{{ rps.valor_csll }}</ValorCsll> |
|||
<IssRetido>{{ rps.iss_retido }}</IssRetido> |
|||
<ValorIss>{{ rps.valor_iss }}</ValorIss> |
|||
<ValorIssRetido>{{ rps.valor_iss_retido }}</ValorIssRetido> |
|||
<OutrasRetencoes>{{ rps.outras_retencoes }}</OutrasRetencoes> |
|||
<BaseCalculo>{{ rps.base_calculo }}</BaseCalculo> |
|||
<Aliquota>{{ rps.aliquota_issqn }}</Aliquota> |
|||
<ValorLiquidoNfse>{{ rps.valor_liquido_nfse }}</ValorLiquidoNfse> |
|||
<DescontoIncondicionado>{{ rps.desconto_incondicionado }}</DescontoIncondicionado> |
|||
<DescontoCondicionado>{{ rps.desconto_condicionado }}</DescontoCondicionado> |
|||
</Valores> |
|||
<ItemListaServico>{{ rps.codigo_servico }}</ItemListaServico> |
|||
<CodigoCnae>{{ rps.cnae_servico }}</CodigoCnae> |
|||
<CodigoTributacaoMunicipio>{{ rps.codigo_tributacao_municipio }}</CodigoTributacaoMunicipio> |
|||
<Discriminacao>{{ rps.descricao }}</Discriminacao> |
|||
<CodigoMunicipio>{{ rps.codigo_municipio }}</CodigoMunicipio> |
|||
</Servico> |
|||
<Prestador> |
|||
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj> |
|||
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal> |
|||
</Prestador> |
|||
<Tomador> |
|||
<IdentificacaoTomador> |
|||
<CpfCnpj> |
|||
{% if rps.tomador.cnpj_cpf|length == 14 %} |
|||
<Cnpj>{{ rps.tomador.cnpj_cpf }}</Cnpj> |
|||
{% endif %} |
|||
{% if rps.tomador.cnpj_cpf|length == 11 %} |
|||
<Cpf>{{ rps.tomador.cnpj_cpf }}</Cpf> |
|||
{% endif %} |
|||
</CpfCnpj> |
|||
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal> |
|||
</IdentificacaoTomador> |
|||
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial> |
|||
<Endereco> |
|||
<Endereco>{{ rps.tomador.logradouro }}</Endereco> |
|||
<Numero>{{ rps.tomador.numero }}</Numero> |
|||
<Complemento>{{ rps.tomador.complemento }}</Complemento> |
|||
<Bairro>{{ rps.tomador.bairro }}</Bairro> |
|||
<CodigoMunicipio>{{ rps.tomador.cidade }}</CodigoMunicipio> |
|||
<Uf>{{ rps.tomador.uf }}</Uf> |
|||
<Cep>{{ rps.tomador.cep }}</Cep> |
|||
</Endereco> |
|||
<Contato> |
|||
<Telefone>{{ rps.tomador.telefone }}</Telefone> |
|||
<Email>{{ rps.tomador.email }}</Email> |
|||
</Contato> |
|||
</Tomador> |
|||
{% if rps.intermediario is defined -%} |
|||
<IntermediarioServico> |
|||
<RazaoSocial>{{ rps.intermediario.razao_social }}</RazaoSocial> |
|||
<CpfCnpj> |
|||
<Cnpj>{{ rps.intermediario.cnpj }}</Cnpj> |
|||
</CpfCnpj> |
|||
<InscricaoMunicipal>{{ rps.intermediario.inscricao_municipal }}</InscricaoMunicipal> |
|||
</IntermediarioServico> |
|||
{% endif %} |
|||
{% if rps.construcao_civil is defined -%} |
|||
<ContrucaoCivil> |
|||
<CodigoObra>{{ rps.construcao_civil.codigo_obra }}</CodigoObra> |
|||
<Art>{{ rps.construcao_civil.art }}</Art> |
|||
</ContrucaoCivil> |
|||
{% endif %} |
|||
</InfRps> |
|||
</Rps> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue