Compare commits
merge into: felipe:master
felipe:feature/betha
felipe:feature/cte
felipe:feature/nfe-4.0
felipe:feature/nfe-mde
felipe:feature/nota-dsf
felipe:fix-endereco
felipe:fix-invalid-token-bug
felipe:fix-url-df
felipe:fix/warning
felipe:iss-net
felipe:master
felipe:master3
felipe:merge-master3
pull from: felipe:feature/nfe-4.0
felipe:feature/betha
felipe:feature/cte
felipe:feature/nfe-4.0
felipe:feature/nfe-mde
felipe:feature/nota-dsf
felipe:fix-endereco
felipe:fix-invalid-token-bug
felipe:fix-url-df
felipe:fix/warning
felipe:iss-net
felipe:master
felipe:master3
felipe:merge-master3
72 Commits
master
...
feature/nf
120 changed files with 3308 additions and 1547 deletions
-
2.gitignore
-
10.travis.yml
-
26README.md
-
7cidades/dsf.md
-
90pytrustnfe/Servidores.py
-
8pytrustnfe/__init__.py
-
13pytrustnfe/certificado.py
-
19pytrustnfe/client.py
-
159pytrustnfe/nfe/__init__.py
-
4pytrustnfe/nfe/assinatura.py
-
8pytrustnfe/nfe/comunicacao.py
-
460pytrustnfe/nfe/danfce.py
-
139pytrustnfe/nfe/danfe.py
-
BINpytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
-
BINpytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
-
4pytrustnfe/nfe/templates/NfeAutorizacao.xml
-
4pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml
-
90pytrustnfe/nfse/assinatura.py
-
2pytrustnfe/nfse/betha/__init__.py
-
82pytrustnfe/nfse/bh/__init__.py
-
44pytrustnfe/nfse/bh/assinatura.py
-
13pytrustnfe/nfse/bh/templates/CancelarNfse.xml
-
11pytrustnfe/nfse/bh/templates/GerarNfse.xml
-
91pytrustnfe/nfse/bh/templates/Rps.xml
-
74pytrustnfe/nfse/carioca/__init__.py
-
13pytrustnfe/nfse/carioca/templates/CancelarNfse.xml
-
3pytrustnfe/nfse/carioca/templates/GerarNfse.xml
-
91pytrustnfe/nfse/carioca/templates/Rps.xml
-
131pytrustnfe/nfse/dsf/__init__.py
-
18pytrustnfe/nfse/dsf/templates/cancelar.xml
-
11pytrustnfe/nfse/dsf/templates/consulta_notas.xml
-
10pytrustnfe/nfse/dsf/templates/consultarLote.xml
-
22pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
-
108pytrustnfe/nfse/dsf/templates/enviar.xml
-
12pytrustnfe/nfse/dsf/templates/soap_header.xml
-
119pytrustnfe/nfse/floripa/__init__.py
-
7pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
-
41pytrustnfe/nfse/floripa/templates/processar_nota.xml
-
35pytrustnfe/nfse/ginfes/__init__.py
-
11pytrustnfe/nfse/imperial/__init__.py
-
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
-
13pytrustnfe/nfse/paulistana/__init__.py
-
2pytrustnfe/nfse/susesu/__init__.py
-
1pytrustnfe/test/XMLs/paulistana_resultado.xml
-
19pytrustnfe/utils.py
-
26pytrustnfe/xml/__init__.py
-
18pytrustnfe/xml/filters.py
-
0pytrustnfe/xml/schemas/enviNFe_v3.10.xsd
-
0pytrustnfe/xml/schemas/leiauteNFe_v3.10.xsd
-
0pytrustnfe/xml/schemas/nfe_v3.10.xsd
-
2pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd
-
0pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd
-
31pytrustnfe/xml/validate.py
-
24requirements.txt
-
32setup.py
-
0tests/XMLs/NFe00000857.xml
-
0tests/XMLs/jinja_remove_empty.xml
-
0tests/XMLs/jinja_result.xml
-
0tests/XMLs/jinja_template.xml
-
0tests/XMLs/paulistana_canc.xml
-
1tests/XMLs/paulistana_canc_errado.xml
-
1tests/XMLs/paulistana_canc_ok.xml
-
1tests/XMLs/paulistana_resultado.xml
-
16tests/XMLs/paulistana_signature.xml
-
0tests/XMLs/recibo_envio_1.xml
-
0tests/XMLs/recibo_envio_2.xml
-
0tests/XMLs/recibo_protocolo_sucesso_1.xml
-
0tests/XMLs/recibo_protocolo_sucesso_2.xml
-
0tests/__init__.py
-
6tests/test_add_qr_code.py
-
12tests/test_assinatura.py
-
8tests/test_certificado.py
-
0tests/test_comunicacao.py
-
2tests/test_consulta_cadastro.py
-
2tests/test_danfe.py
-
2tests/test_ginfes.py
-
8tests/test_nfse_paulistana.py
-
0tests/test_servidores.py
-
16tests/test_utils.py
-
4tests/test_xml.py
-
5tests/test_xml_serializacao.py
-
0tests/teste.pfx
-
0tests/xml_assinado.xml
-
6tests/xml_com_qrcode.xml
-
6tests/xml_sem_qrcode.xml
-
0tests/xml_valido_assinado.xml
@ -0,0 +1,7 @@ |
|||
* Belém - PA |
|||
* Sorocaba - SP |
|||
* Teresina - PI |
|||
* Campinas - SP |
|||
* Uberlandia - MG |
|||
* São Luis - MA |
|||
* Campo Grande - MS |
|||
@ -0,0 +1,460 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2017 Johny Chen Jy, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import re |
|||
from textwrap import wrap |
|||
from io import BytesIO |
|||
|
|||
from reportlab.lib import utils |
|||
from reportlab.pdfgen import canvas |
|||
from reportlab.lib.units import cm, mm |
|||
from reportlab.graphics.barcode import qr |
|||
from reportlab.graphics import renderPDF |
|||
from reportlab.graphics.shapes import Drawing |
|||
from reportlab.platypus import Table, TableStyle, Paragraph, Image |
|||
from reportlab.lib.enums import TA_CENTER |
|||
from reportlab.lib.styles import ParagraphStyle |
|||
|
|||
|
|||
def format_cnpj_cpf(value): |
|||
if len(value) < 12: # CPF |
|||
cValue = '%s.%s.%s-%s' % (value[:-8], value[-8:-5], |
|||
value[-5:-2], value[-2:]) |
|||
else: |
|||
cValue = '%s.%s.%s/%s-%s' % (value[:-12], value[-12:-9], |
|||
value[-9:-6], value[-6:-2], value[-2:]) |
|||
return cValue |
|||
|
|||
|
|||
def getdateUTC(cDateUTC): |
|||
cDt = cDateUTC[0:10].split('-') |
|||
cDt.reverse() |
|||
return '/'.join(cDt), cDateUTC[11:16] |
|||
|
|||
|
|||
def format_number(cNumber, precision=0, group_sep='.', decimal_sep=','): |
|||
if cNumber: |
|||
number = float(cNumber) |
|||
return ("{:,." + str(precision) + "f}").format(number).\ |
|||
replace(",", "X").replace(".", ",").replace("X", ".") |
|||
return "" |
|||
|
|||
|
|||
def tagtext(oNode=None, cTag=None): |
|||
try: |
|||
xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag) |
|||
cText = oNode.find(xpath).text |
|||
except: |
|||
cText = '' |
|||
return cText |
|||
|
|||
|
|||
def get_image(path, width=1 * cm): |
|||
img = utils.ImageReader(path) |
|||
iw, ih = img.getSize() |
|||
aspect = ih / float(iw) |
|||
return Image(path, width=width, height=(width * aspect)) |
|||
|
|||
|
|||
def format_telefone(telefone): |
|||
telefone = re.sub('[^0-9]', '', telefone) |
|||
if len(telefone) == 10: |
|||
telefone = '(%s) %s-%s' % (telefone[0:2], |
|||
telefone[2:6], |
|||
telefone[6:]) |
|||
elif len(telefone) == 11: |
|||
telefone = '(%s) %s-%s' % (telefone[0:2], |
|||
telefone[2:7], |
|||
telefone[7:]) |
|||
return telefone |
|||
|
|||
|
|||
class danfce(object): |
|||
|
|||
def __init__(self, list_xml, logo=None): |
|||
|
|||
self.current_font_size = 7 |
|||
self.current_font_name = 'NimbusSanL-Regu' |
|||
|
|||
self.max_height = 840 |
|||
self.min_height = 1 |
|||
self.min_width = 5 |
|||
self.max_width = 200 |
|||
self.current_height = 840 |
|||
|
|||
self.oPDF_IO = BytesIO() |
|||
self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=(7.2 * cm, 30 * cm)) |
|||
self.canvas.setTitle('DANFCE') |
|||
self.canvas.setLineWidth(.5) |
|||
self.canvas.setFont(self.current_font_name, self.current_font_size) |
|||
|
|||
self.list_xml = list_xml |
|||
self.logo = logo |
|||
|
|||
self.nfce_generate() |
|||
|
|||
def ide_emit(self, oXML=None): |
|||
|
|||
elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") |
|||
|
|||
# Razão Social emitente |
|||
nomeEmpresa = tagtext(oNode=elem_emit, cTag='xFant') |
|||
self.drawTitle(nomeEmpresa, 10) |
|||
|
|||
if self.logo: |
|||
img = get_image(self.logo, width=10 * mm) |
|||
img.drawOn(self.canvas, 5, 830) |
|||
|
|||
cEnd = tagtext(oNode=elem_emit, cTag="xNome") + '<br />' |
|||
cEnd += "CNPJ: %s " % (format_cnpj_cpf( |
|||
tagtext(oNode=elem_emit, cTag='CNPJ'))) |
|||
cEnd += "IE: %s" % (tagtext(oNode=elem_emit, cTag="IE")) + '<br />' |
|||
cEnd += tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( |
|||
oNode=elem_emit, cTag='nro') + ' - ' |
|||
cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '<br />' + tagtext( |
|||
oNode=elem_emit, cTag='xMun') + ' - ' |
|||
cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext( |
|||
oNode=elem_emit, cTag='CEP') + '<br />' |
|||
cEnd += 'Fone: ' + format_telefone(tagtext( |
|||
oNode=elem_emit, cTag='fone')) |
|||
|
|||
self._drawCenteredParagraph(cEnd) |
|||
self.drawLine() |
|||
|
|||
def danfce_information(self): |
|||
self.drawTitle( |
|||
"DANFE NFC-e - Documento Auxiliar da Nota Fiscal de", |
|||
7, 'NimbusSanL-Bold') |
|||
|
|||
self.drawTitle("Consumidor Eletrônica", 7, 'NimbusSanL-Bold') |
|||
|
|||
self.drawString( |
|||
"NFC-e não permite aproveitamento de crédito de ICMS", True) |
|||
self.drawLine() |
|||
|
|||
def produtos(self, oXML=None, el_det=None, oPaginator=None, |
|||
list_desc=None, list_cod_prod=None): |
|||
|
|||
rows = [['Cód', 'Descrição', 'Qtde', 'Un', 'Unit.', 'Total']] |
|||
colWidths = (25, 90, 15, 15, 25, 25) |
|||
rowHeights = [7] |
|||
|
|||
for id in range(oPaginator[0], oPaginator[1]): |
|||
|
|||
item = el_det[id] |
|||
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") |
|||
|
|||
cod = tagtext(oNode=el_prod, cTag='cProd') |
|||
descricao = tagtext(oNode=el_prod, cTag='xProd') |
|||
descricao = (descricao[:20] + '..') if len(descricao) > 20 else descricao |
|||
Un = tagtext(oNode=el_prod, cTag='uCom') |
|||
Un = (Un[:2]) if len(Un) > 2 else Un |
|||
qtde = format_number(tagtext(oNode=el_prod, cTag='qCom'), |
|||
precision=2) |
|||
vl_unit = format_number(tagtext(oNode=el_prod, cTag='vUnCom'), |
|||
precision=2) |
|||
vl_total = format_number( |
|||
tagtext(oNode=el_prod, cTag='vProd'), precision=2) |
|||
|
|||
new_row = [cod, descricao, qtde, Un, vl_unit, vl_total] |
|||
|
|||
rows.append(new_row) |
|||
rowHeights.append(self.current_font_size + 2) |
|||
|
|||
self._draw_product_table(rows, colWidths, rowHeights) |
|||
|
|||
def _draw_product_table(self, rows, colWidths, rowHeights): |
|||
table = Table(rows, colWidths, tuple(rowHeights)) |
|||
table.setStyle(TableStyle([ |
|||
('FONTSIZE', (0, 0), (-1, -1), 7), |
|||
('FONT', (0, 1), (-1, -1), 'NimbusSanL-Regu'), |
|||
('FONT', (0, 0), (-1, 0), 'NimbusSanL-Bold'), |
|||
('ALIGN', (0, 0), (-1, 0), "LEFT"), |
|||
('ALIGN', (1, 0), (-1, 0), "LEFT"), |
|||
('ALIGN', (2, 0), (-1, 0), "CENTER"), |
|||
('ALIGN', (3, 0), (-1, 0), "CENTER"), |
|||
('ALIGN', (0, 1), (-1, -1), "LEFT"), |
|||
('ALIGN', (1, 1), (-1, -1), "LEFT"), |
|||
('ALIGN', (2, 1), (-1, -1), "CENTER"), |
|||
('ALIGN', (3, 1), (-1, -1), "CENTER"), |
|||
])) |
|||
|
|||
w, h = table.wrapOn(self.canvas, 200, 450) |
|||
table.drawOn(self.canvas, 0, self.current_height - (h * 1.2)) |
|||
self.current_height -= (h * 1.1) |
|||
|
|||
def totais(self, oXML=None): |
|||
# Impostos |
|||
el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") |
|||
|
|||
total_tributo = format_number(tagtext(oNode=el_total, cTag='vTotTrib'), |
|||
precision=2) |
|||
valor_total = format_number(tagtext(oNode=el_total, cTag='vProd'), |
|||
precision=2) |
|||
desconto = format_number(tagtext(oNode=el_total, cTag='vDesc'), |
|||
precision=2) |
|||
valor_a_pagar = format_number(tagtext(oNode=el_total, cTag='vNF'), |
|||
precision=2) |
|||
el_pag = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}pag") |
|||
troco = format_number(tagtext(oNode=el_pag, cTag="vTroco")) |
|||
|
|||
payment_method_list = {'01': 'Dinheiro', |
|||
'02': 'Cheque', |
|||
'03': 'Cartão de Crédito', |
|||
'04': 'Cartão de Débito', |
|||
"05": "Crédito Loja", |
|||
'10': 'Vale Alimentação', |
|||
'11': 'Vale Refeição', |
|||
'12': 'Vale Presente', |
|||
'13': 'Vale Combustível', |
|||
'14': 'Duplicata Mercantil', |
|||
'15': 'Boleto Bancario', |
|||
'90': 'Sem Pagamento', |
|||
'99': 'Outros'} |
|||
quant_produtos = len(oXML.findall( |
|||
".//{http://www.portalfiscal.inf.br/nfe}det")) |
|||
|
|||
payment_methods = [] |
|||
|
|||
for pagId, item in enumerate(el_pag): |
|||
if 'tPag' not in item.tag: |
|||
continue |
|||
|
|||
payment = [] |
|||
method = payment_method_list[item.text] |
|||
|
|||
payment.append(method) |
|||
payment.append(format_number(item.getnext().text, precision=2)) |
|||
payment_methods.append(payment) |
|||
|
|||
values = {'quantidade_itens': quant_produtos, |
|||
'total_tributo': total_tributo, |
|||
'valor_total': valor_total, |
|||
'desconto': desconto, |
|||
'valor_a_pagar': valor_a_pagar, |
|||
'formas_de_pagamento': payment_methods, |
|||
'troco': troco, |
|||
} |
|||
|
|||
self.draw_totals_table(values) |
|||
|
|||
self.drawLine() |
|||
|
|||
def draw_totals_table(self, values): |
|||
rowHeights = [7, 7, 7, 7, 10] |
|||
data = [['QTD.TOTAL DE ITENS', values['quantidade_itens']], |
|||
['VALOR TOTAL R$', values['valor_total']], |
|||
['DESCONTO R$', values['desconto']], |
|||
['VALOR A PAGAR R$', values['valor_a_pagar']], |
|||
['FORMA DE PAGAMENTO', 'VALOR PAGO R$'], |
|||
] |
|||
|
|||
for item in values['formas_de_pagamento']: |
|||
data.append([item[0], item[1]]) |
|||
rowHeights.append(7) |
|||
data.append(['TROCO', format_number(values['troco'], precision=2)]) |
|||
rowHeights.append(7) |
|||
|
|||
table2 = Table(data, colWidths=(150, 50), rowHeights=tuple(rowHeights)) |
|||
table2.setStyle(TableStyle([ |
|||
('FONTSIZE', (0, 0), (-1, -1), 7), |
|||
('FONT', (0, 0), (1, -1), 'NimbusSanL-Regu'), |
|||
('FONT', (0, 4), (1, 4), 'NimbusSanL-Bold'), |
|||
('ALIGN', (1, 0), (1, -1), "RIGHT") |
|||
])) |
|||
w, h = table2.wrapOn(self.canvas, 200, 450) |
|||
table2.drawOn(self.canvas, 0, self.current_height - (h * 1.1)) |
|||
self.current_height -= h |
|||
|
|||
def inf_authentication(self, oXML=None): |
|||
el_infNFe = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}infNFe") |
|||
# n nfce, serie e data de solicitacao |
|||
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") |
|||
|
|||
el_NFeSupl = oXML.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}infNFeSupl") |
|||
|
|||
el_dest = el_infNFe.find(".//{http://www.portalfiscal.inf.br/nfe}dest") |
|||
# chave, n protocolo, data autorizacao |
|||
el_prot_nfe = oXML.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}protNFe") |
|||
|
|||
el_infAdic = oXML.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}infAdic") |
|||
|
|||
url_chave = tagtext(oNode=el_NFeSupl, cTag='urlChave') |
|||
access_key = tagtext(oNode=el_prot_nfe, cTag="chNFe") |
|||
|
|||
frase_chave_acesso = 'Consulte pela Chave de Acesso em:<br />\ |
|||
%s<br />%s' % (url_chave, access_key) |
|||
|
|||
qrcode = tagtext(oNode=el_NFeSupl, cTag='qrCode') |
|||
|
|||
cnpj = tagtext(oNode=el_dest, cTag='CNPJ') |
|||
cpf = tagtext(oNode=el_dest, cTag='CPF') |
|||
if cnpj: |
|||
cnpj_cpf = format_cnpj_cpf(cnpj) |
|||
cnpj_cpf = "CONSUMIDOR CNPJ: %s" % (cnpj) |
|||
elif cpf: |
|||
cnpj_cpf = format_cnpj_cpf(cpf) |
|||
cnpj_cpf = "CONSUMIDOR CPF: %s" % (cpf) |
|||
else: |
|||
cnpj_cpf = u"CONSUMIDOR NÃO IDENTIFICADO" |
|||
|
|||
nNFC = tagtext(oNode=el_ide, cTag="nNF") |
|||
serie = tagtext(oNode=el_ide, cTag='serie') |
|||
|
|||
dataSolicitacao = getdateUTC(tagtext(oNode=el_ide, cTag="dhEmi")) |
|||
dataSolicitacao = dataSolicitacao[0] + " " + dataSolicitacao[1] |
|||
|
|||
numProtocolo = tagtext(oNode=el_prot_nfe, cTag="nProt") |
|||
|
|||
dataAutorizacao = getdateUTC(tagtext(oNode=el_prot_nfe, |
|||
cTag='dhRecbto')) |
|||
dataAutorizacao = dataAutorizacao[0] + " " + dataAutorizacao[1] |
|||
|
|||
text = u"%s <br />%s <br />NFC-e nº%s Série %s %s<br />\ |
|||
Protocolo de autorização: %s<br />Data de autorização %s<br />\ |
|||
" % (frase_chave_acesso, cnpj_cpf, nNFC, serie, dataSolicitacao, |
|||
numProtocolo, dataAutorizacao) |
|||
|
|||
self._drawCenteredParagraph(text) |
|||
|
|||
self.draw_qr_code(qrcode) |
|||
|
|||
infAdFisco = tagtext(oNode=el_infAdic, cTag='infAdFisco') |
|||
self._drawCenteredParagraph(infAdFisco) |
|||
|
|||
infCpl = tagtext(oNode=el_infAdic, cTag='infCpl') |
|||
self._drawCenteredParagraph(infCpl) |
|||
|
|||
def _drawCenteredParagraph(self, text): |
|||
|
|||
style = ParagraphStyle( |
|||
name='Normal', |
|||
fontName='NimbusSanL-Regu', |
|||
fontSize=7, |
|||
alignment=TA_CENTER, |
|||
leading=7, |
|||
) |
|||
|
|||
paragraph = Paragraph(text, style=style) |
|||
w, h = paragraph.wrapOn(self.canvas, 180, 300) |
|||
paragraph.drawOn(self.canvas, 10, self.current_height - h) |
|||
self.current_height -= (h*1.1) |
|||
|
|||
def drawString(self, string, centered=False): |
|||
if centered: |
|||
self.canvas.drawCentredString( |
|||
self.max_width / 2, self.current_height, string) |
|||
self.current_height -= self.current_font_size |
|||
else: |
|||
self.canvas.drawString(self.min_width, self.current_height, string) |
|||
self.current_height -= self.current_font_size |
|||
|
|||
def drawTitle(self, string, size, font='NimbusSanL-Regu'): |
|||
self.canvas.setFont(font, size) |
|||
self.canvas.drawCentredString( |
|||
self.max_width / 2, self.current_height, string) |
|||
self.current_height -= self.current_font_size |
|||
self.canvas.setFont(self.current_font_name, self.current_font_size) |
|||
|
|||
def drawLine(self): |
|||
self.canvas.line(self.min_width, self.current_height, |
|||
self.max_width, self.current_height) |
|||
self.current_height -= self.current_font_size |
|||
|
|||
def draw_qr_code(self, string): |
|||
qr_code = qr.QrCodeWidget(string) |
|||
drawing = Drawing(23 * mm, 23 * mm) |
|||
drawing.add(qr_code) |
|||
renderPDF.draw(drawing, self.canvas, 20 * mm, self.current_height - 85) |
|||
self.current_height -= 85 |
|||
|
|||
def newpage(self): |
|||
self.current_height = self.max_height |
|||
self.Page += 1 |
|||
self.canvas.showPage() |
|||
|
|||
def nfce_generate(self): |
|||
for oXML in self.list_xml: |
|||
oXML_cobr = oXML.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}cobr") |
|||
|
|||
self.NrPages = 1 |
|||
self.Page = 1 |
|||
|
|||
# Calculando total linhas usadas para descrições dos itens |
|||
# Com bloco fatura, apenas 29 linhas para itens na primeira folha |
|||
nNr_Lin_Pg_1 = 34 if oXML_cobr is None else 30 |
|||
# [ rec_ini , rec_fim , lines , limit_lines ] |
|||
oPaginator = [[0, 0, 0, nNr_Lin_Pg_1]] |
|||
el_det = oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det") |
|||
if el_det is not None: |
|||
list_desc = [] |
|||
list_cod_prod = [] |
|||
nPg = 0 |
|||
for nId, item in enumerate(el_det): |
|||
el_prod = item.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}prod") |
|||
infAdProd = item.find( |
|||
".//{http://www.portalfiscal.inf.br/nfe}infAdProd") |
|||
|
|||
list_ = wrap(tagtext(oNode=el_prod, cTag='xProd'), 56) |
|||
if infAdProd is not None: |
|||
list_.extend(wrap(infAdProd.text, 56)) |
|||
list_desc.append(list_) |
|||
|
|||
list_cProd = wrap(tagtext(oNode=el_prod, cTag='cProd'), 14) |
|||
list_cod_prod.append(list_cProd) |
|||
|
|||
# Nr linhas necessárias p/ descrição item |
|||
nLin_Itens = len(list_) |
|||
|
|||
if (oPaginator[nPg][2] + nLin_Itens) >= oPaginator[nPg][3]: |
|||
oPaginator.append([0, 0, 0, 77]) |
|||
nPg += 1 |
|||
oPaginator[nPg][0] = nId |
|||
oPaginator[nPg][1] = nId + 1 |
|||
oPaginator[nPg][2] = nLin_Itens |
|||
else: |
|||
# adiciona-se 1 pelo funcionamento de xrange |
|||
oPaginator[nPg][1] = nId + 1 |
|||
oPaginator[nPg][2] += nLin_Itens |
|||
|
|||
self.NrPages = len(oPaginator) # Calculando nr. páginas |
|||
|
|||
self.ide_emit(oXML=oXML) |
|||
# self.destinatario(oXML=oXML) |
|||
self.danfce_information() |
|||
|
|||
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0], |
|||
list_desc=list_desc, list_cod_prod=list_cod_prod) |
|||
|
|||
self.drawLine() |
|||
|
|||
self.totais(oXML=oXML) |
|||
|
|||
self.inf_authentication(oXML=oXML) |
|||
|
|||
# Gera o restante das páginas do XML |
|||
for oPag in oPaginator[1:]: |
|||
if oPag: |
|||
self.newpage() |
|||
self.ide_emit(oXML=oXML) |
|||
# self.destinatario(oXML=oXML) |
|||
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag, |
|||
list_desc=list_desc, |
|||
list_cod_prod=list_cod_prod) |
|||
self.totais(oXML=oXML) |
|||
self.inf_authentication(oXML=oXML) |
|||
|
|||
self.newpage() |
|||
|
|||
self.canvas.save() |
|||
|
|||
def writeto_pdf(self, fileObj): |
|||
pdf_out = self.oPDF_IO.getvalue() |
|||
self.oPDF_IO.close() |
|||
fileObj.write(pdf_out) |
|||
@ -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.bh.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://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 = '<?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': xml_send, |
|||
'received_xml': response.decode('utf-8'), |
|||
'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> |
|||
@ -0,0 +1,74 @@ |
|||
# © 2018 Danimar Ribeiro, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import os |
|||
import suds |
|||
from pytrustnfe.client import get_authenticated_client |
|||
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://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl' |
|||
else: |
|||
base_url = 'https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl' # noqa |
|||
|
|||
xml_send = kwargs["xml"].decode('utf-8') |
|||
cert, key = extract_cert_and_key_from_pfx( |
|||
certificado.pfx, certificado.password) |
|||
cert, key = save_cert_key(cert, key) |
|||
client = get_authenticated_client(base_url, cert, key) |
|||
|
|||
try: |
|||
response = getattr(client.service, method)(xml_send) |
|||
except suds.WebFault as e: |
|||
return { |
|||
'sent_xml': str(xml_send), |
|||
'received_xml': str(e.fault.faultstring), |
|||
'object': None |
|||
} |
|||
|
|||
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) |
|||
@ -0,0 +1,13 @@ |
|||
<CancelarNfseEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd"> |
|||
<Pedido> |
|||
<InfPedidoCancelamento Id="Cancelamento_NF{{ 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,3 @@ |
|||
<GerarNfseEnvio xmlns="http://notacarioca.rio.gov.br/WSNacional/XSD/1/nfse_pcrj_v01.xsd"> |
|||
{% include 'Rps.xml' %} |
|||
</GerarNfseEnvio> |
|||
@ -0,0 +1,91 @@ |
|||
<Rps> |
|||
<InfRps xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" Id="r{{ 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> |
|||
@ -0,0 +1,131 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
# © 2017 Fábio Luna, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import os |
|||
import suds |
|||
from lxml import etree |
|||
from pytrustnfe.xml import render_xml, sanitize_response |
|||
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key |
|||
from pytrustnfe.nfse.assinatura import Assinatura |
|||
from pytrustnfe.client import get_client |
|||
|
|||
|
|||
def _render(certificado, method, **kwargs): |
|||
path = os.path.join(os.path.dirname(__file__), 'templates') |
|||
if method == "testeEnviar": |
|||
xml_send = render_xml(path, 'enviar.xml', True, **kwargs) |
|||
else: |
|||
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs) |
|||
|
|||
if type(xml_send) != str: |
|||
xml_send = etree.tostring(xml_send) |
|||
|
|||
return xml_send |
|||
|
|||
|
|||
def _get_url(**kwargs): |
|||
|
|||
try: |
|||
cod_cidade = kwargs['nfse']['cidade'] |
|||
except (KeyError, TypeError): |
|||
raise KeyError("Código de cidade inválido!") |
|||
|
|||
urls = { |
|||
# Belém - PA |
|||
'2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws', |
|||
# Sorocaba - SP |
|||
'7145': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', |
|||
# Teresina - PI |
|||
'1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', |
|||
# Campinas - SP |
|||
'6291': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', |
|||
# Uberlandia - MG |
|||
'5403': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', |
|||
# São Luis - MA |
|||
'0921': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', |
|||
# Campo Grande - MS |
|||
'2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl', |
|||
} |
|||
|
|||
try: |
|||
return urls[str(cod_cidade)] |
|||
except KeyError: |
|||
raise KeyError("DSF não emite notas da cidade {}!".format( |
|||
cod_cidade)) |
|||
|
|||
|
|||
def _send(certificado, method, **kwargs): |
|||
url = _get_url(**kwargs) |
|||
|
|||
path = os.path.join(os.path.dirname(__file__), 'templates') |
|||
|
|||
xml_send = _render(path, method, **kwargs) |
|||
client = get_client(url) |
|||
response = False |
|||
|
|||
if certificado: |
|||
cert, key = extract_cert_and_key_from_pfx( |
|||
certificado.pfx, certificado.password) |
|||
cert, key = save_cert_key(cert, key) |
|||
signer = Assinatura(cert, key, certificado.password) |
|||
xml_send = signer.assina_xml(xml_send, '') |
|||
|
|||
try: |
|||
response = getattr(client.service, method)(xml_send) |
|||
response, obj = sanitize_response(response.encode()) |
|||
except suds.WebFault as e: |
|||
return { |
|||
'sent_xml': xml_send, |
|||
'received_xml': e.fault.faultstring, |
|||
'object': None |
|||
} |
|||
except Exception as e: |
|||
if response: |
|||
raise Exception(response) |
|||
else: |
|||
raise e |
|||
|
|||
return { |
|||
'sent_xml': xml_send, |
|||
'received_xml': response, |
|||
'object': obj |
|||
} |
|||
|
|||
|
|||
def xml_enviar(certificado, **kwargs): |
|||
return _render(certificado, 'enviar', **kwargs) |
|||
|
|||
|
|||
def enviar(certificado, **kwargs): |
|||
if "xml" not in kwargs: |
|||
kwargs['xml'] = xml_enviar(certificado, **kwargs) |
|||
return _send(certificado, 'enviar', **kwargs) |
|||
|
|||
|
|||
def xml_teste_enviar(certificado, **kwargs): |
|||
return _render(certificado, 'testeEnviar', **kwargs) |
|||
|
|||
|
|||
def teste_enviar(certificado, **kwargs): |
|||
if "xml" not in kwargs: |
|||
kwargs['xml'] = xml_teste_enviar(certificado, **kwargs) |
|||
return _send(certificado, 'testeEnviar', **kwargs) |
|||
|
|||
|
|||
def cancelar(certificado, ** kwargs): |
|||
return _send(certificado, 'cancelar', **kwargs) |
|||
|
|||
|
|||
def consulta_lote(**kwargs): |
|||
return _send(False, 'consultarLote', **kwargs) |
|||
|
|||
|
|||
def xml_consultar_nfse_rps(certificado, **kwargs): |
|||
return _render(certificado, 'consultarNFSeRps', **kwargs) |
|||
|
|||
|
|||
def consultar_nfse_rps(certificado, **kwargs): |
|||
if "xml" not in kwargs: |
|||
kwargs['xml'] = xml_consultar_nfse_rps(certificado, **kwargs) |
|||
return _send(certificado, 'consultarNFSeRps', **kwargs) |
|||
@ -0,0 +1,18 @@ |
|||
<ns1:ReqCancelamentoNFSe xmlns:ns1="http://localhost:8080/WsNFe2/lote" |
|||
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqCancelamentoNFSe.xsd"> |
|||
<Cabecalho> |
|||
<CodCidade>{{ cancelamento.cidade }}</CodCidade> |
|||
<CPFCNPJRemetente>{{ cancelamento.cpf_cnpj }}</CPFCNPJRemetente> |
|||
<transacao>true</transacao> |
|||
<Versao>1</Versao> |
|||
</Cabecalho> |
|||
<Lote Id="lote:1ABCDZ"> |
|||
<Nota Id="nota:{{ cancelamento.nota_id }}"> |
|||
<InscricaoMunicipalPrestador>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipalPrestador> |
|||
<NumeroNota>{{ cancelamento.nota_id }}</NumeroNota> |
|||
<CodigoVerificacao>{{ cancelamento.assinatura }}</CodigoVerificacao> |
|||
<MotivoCancelamento>{{ cancelamento.motivo }}</MotivoCancelamento> |
|||
</Nota> |
|||
</Lote> |
|||
</ns1:ReqCancelamentoNFSe> |
|||
@ -0,0 +1,11 @@ |
|||
<ns1:ReqConsultaNotas xmlns:ns1="http://localhost:8080/WsNFe2/lote" xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaNotas.xsd"> |
|||
<Cabecalho Id="Consulta:notas"> |
|||
<CodCidade>{{ consulta.cidade }}</CodCidade> |
|||
<CPFCNPJRemetente>{{ consulta.cpf_cnpj }}</CPFCNPJRemetente> |
|||
<InscricaoMunicipalPrestador>{{ consulta.inscricao_municipal }}</InscricaoMunicipalPrestador> |
|||
<dtInicio>{{ consulta.data_inicio }}</dtInicio> |
|||
<dtFim>{{ consulta.data_final }}</dtFim> |
|||
<NotaInicial>{{ consulta.nota_inicial }}</NotaInicial> |
|||
<Versao>1</Versao> |
|||
</Cabecalho> |
|||
</ns1:ReqConsultaNotas> |
|||
@ -0,0 +1,10 @@ |
|||
<ns1:ReqConsultaLote xmlns:ns1="http://localhost:8080/WsNFe2/lote" |
|||
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaLote.xsd"> |
|||
<Cabecalho> |
|||
<CodCidade>{{ consulta.cidade }}</CodCidade> |
|||
<CPFCNPJRemetente>{{ consulta.cpf_cnpj }}</CPFCNPJRemetente> |
|||
<Versao>1</Versao> |
|||
<NumeroLote>{{ consulta.lote }}</NumeroLote> |
|||
</Cabecalho> |
|||
</ns1:ReqConsultaLote> |
|||
@ -0,0 +1,22 @@ |
|||
<ns1:ReqConsultaNFSeRPS |
|||
xmlns:ns1="http://localhost:8080/WsNFe2/lote" |
|||
xmlns:tipos="http://localhost:8080/WsNFe2/tp" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaNFSeRPS.xsd"> |
|||
<Cabecalho> |
|||
<CodCidade>{{ nfse.cidade }}</CodCidade> |
|||
<CPFCNPJRemetente>{{ nfse.cpf_cnpj }}</CPFCNPJRemetente> |
|||
<transacao>true</transacao> |
|||
<Versao>1</Versao> |
|||
</Cabecalho> |
|||
<Lote Id="lote:{{ nfse.lote }}"> |
|||
{% for rps in nfse.lista_rps -%} |
|||
<RPSConsulta> |
|||
<RPS Id="rps:{{ rps.numero }}"> |
|||
<InscricaoMunicipalPrestador>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipalPrestador> |
|||
<NumeroRPS>{{ rps.numero }}</NumeroRPS> |
|||
<SeriePrestacao>{{ rps.serie_prestacao }}</SeriePrestacao> |
|||
</RPS> |
|||
</RPSConsulta> |
|||
{% endfor %} |
|||
</Lote> |
|||
</ns1:ReqConsultaNFSeRPS> |
|||
@ -0,0 +1,108 @@ |
|||
<ns1:ReqEnvioLoteRPS xmlns:ns1="http://localhost:8080/WsNFe2/lote" |
|||
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqEnvioLoteRPS.xsd"> |
|||
<Cabecalho> |
|||
<CodCidade>{{ nfse.cidade }}</CodCidade> |
|||
<CPFCNPJRemetente>{{ nfse.cpf_cnpj }}</CPFCNPJRemetente> |
|||
<RazaoSocialRemetente>{{ nfse.remetente }}</RazaoSocialRemetente> |
|||
<transacao>{{ nfse.transacao }}</transacao> |
|||
<dtInicio>{{ nfse.data_inicio|format_date }}</dtInicio> |
|||
<dtFim>{{ nfse.data_fim|format_date }}</dtFim> |
|||
<QtdRPS>{{ nfse.total_rps }}</QtdRPS> |
|||
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos> |
|||
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes> |
|||
<Versao>1</Versao> |
|||
<MetodoEnvio>WS</MetodoEnvio> |
|||
</Cabecalho> |
|||
<Lote Id="{{ nfse.lote_id }}"> |
|||
{% for rps in nfse.lista_rps -%} |
|||
<RPS Id="{{ rps.numero }}"> |
|||
<Assinatura>{{ rps.assinatura }}</Assinatura> |
|||
<InscricaoMunicipalPrestador>{{ rps.prestador.inscricao_municipal }} |
|||
</InscricaoMunicipalPrestador> |
|||
<RazaoSocialPrestador>{{ rps.prestador.razao_social }}</RazaoSocialPrestador> |
|||
<TipoRPS>RPS</TipoRPS> |
|||
<SerieRPS>{{ rps.serie }}</SerieRPS> |
|||
<NumeroRPS>{{ rps.numero }}</NumeroRPS> |
|||
<DataEmissaoRPS>{{ rps.data_emissao|format_datetime }} |
|||
</DataEmissaoRPS> |
|||
<SituacaoRPS>{{ rps.situacao }}</SituacaoRPS> |
|||
<SerieRPSSubstituido></SerieRPSSubstituido> |
|||
<NumeroRPSSubstituido>0</NumeroRPSSubstituido> |
|||
<NumeroNFSeSubstituida>0</NumeroNFSeSubstituida> |
|||
<DataEmissaoNFSeSubstituida>1900-01-01</DataEmissaoNFSeSubstituida> |
|||
<SeriePrestacao>{{ rps.serie_prestacao }}</SeriePrestacao> |
|||
<InscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipalTomador> |
|||
<CPFCNPJTomador>{{ rps.tomador.cpf_cnpj }}</CPFCNPJTomador> |
|||
<RazaoSocialTomador>{{ rps.tomador.razao_social }} |
|||
</RazaoSocialTomador> |
|||
<TipoLogradouroTomador>{{ rps.tomador.tipo_logradouro }} |
|||
</TipoLogradouroTomador> |
|||
<LogradouroTomador>{{ rps.tomador.logradouro }}</LogradouroTomador> |
|||
<NumeroEnderecoTomador>{{ rps.tomador.numero }} |
|||
</NumeroEnderecoTomador> |
|||
<TipoBairroTomador>{{ rps.tomador.tipo_bairro }}</TipoBairroTomador> |
|||
<BairroTomador>{{ rps.tomador.bairro }}</BairroTomador> |
|||
<CidadeTomador>{{ rps.tomador.cidade }}</CidadeTomador> |
|||
<CidadeTomadorDescricao>{{ rps.tomador.cidade_descricao }} |
|||
</CidadeTomadorDescricao> |
|||
<CEPTomador>{{ rps.tomador.cep }}</CEPTomador> |
|||
<EmailTomador>{{ rps.tomador.email }}</EmailTomador> |
|||
<CodigoAtividade>{{ rps.codigo_atividade }}</CodigoAtividade> |
|||
<AliquotaAtividade>{{ rps.aliquota_atividade }}</AliquotaAtividade> |
|||
<TipoRecolhimento>{{ rps.tipo_recolhimento }}</TipoRecolhimento> |
|||
<MunicipioPrestacao>{{ rps.municipio_prestacao }} |
|||
</MunicipioPrestacao> |
|||
<MunicipioPrestacaoDescricao>{{ rps.municipio_descricao_prestacao }} |
|||
</MunicipioPrestacaoDescricao> |
|||
<Operacao>{{ rps.operacao }}</Operacao> |
|||
<Tributacao>{{ rps.tributacao }}</Tributacao> |
|||
<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> |
|||
<AliquotaPIS>{{ rps.aliquota_pis }}</AliquotaPIS> |
|||
<AliquotaCOFINS>{{ rps.aliquota_cofins }}</AliquotaCOFINS> |
|||
<AliquotaINSS>{{ rps.aliquota_inss }}</AliquotaINSS> |
|||
<AliquotaIR>{{ rps.aliquota_ir }}</AliquotaIR> |
|||
<AliquotaCSLL>{{ rps.aliquota_csll }}</AliquotaCSLL> |
|||
<DescricaoRPS>{{ rps.descricao }}</DescricaoRPS> |
|||
<DDDPrestador>{{ rps.prestador.ddd }}</DDDPrestador> |
|||
<TelefonePrestador>{{ rps.prestador.telefone }}</TelefonePrestador> |
|||
<DDDTomador>{{ rps.tomador.ddd }}</DDDTomador> |
|||
<TelefoneTomador>{{ rps.tomador.telefone }}</TelefoneTomador> |
|||
<MotCancelamento>{{ rps.motivo_cancelamento }}</MotCancelamento> |
|||
{% if rps.deducoes|count > 0 %} |
|||
<Deducoes> |
|||
{% for deducao in rps.deducoes -%} |
|||
<Deducao> |
|||
<DeducaoPor>{{ deducao.por }}</DeducaoPor> |
|||
<TipoDeducao>{{ deducao.tipo }}</TipoDeducao> |
|||
<CPFCNPJReferencia>{{ deducao.cnpj_referencia }}</CPFCNPJReferencia> |
|||
<NumeroNFReferencia>{{ deducao.nf_referencia }}</NumeroNFReferencia> |
|||
<ValorTotalReferencia>{{ deducao.valor_referencia }}</ValorTotalReferencia> |
|||
<PercentualDeduzir>{{ deducao.percentual_deduzir }}</PercentualDeduzir> |
|||
<ValorDeduzir>{{ deducao.valor_deduzir }}</ValorDeduzir> |
|||
</Deducao> |
|||
{% endfor %} |
|||
</Deducoes> |
|||
{% endif %} |
|||
{% if rps.deducoes|count == 0 %} |
|||
<Deducoes /> |
|||
{% endif %} |
|||
<Itens> |
|||
{% for item in rps.itens -%} |
|||
<Item> |
|||
<DiscriminacaoServico>{{ item.descricao }}</DiscriminacaoServico> |
|||
<Quantidade>{{ item.quantidade }}</Quantidade> |
|||
<ValorUnitario>{{ item.valor_unitario }}</ValorUnitario> |
|||
<ValorTotal>{{ item.valor_total }}</ValorTotal> |
|||
<Tributavel>S</Tributavel> |
|||
</Item> |
|||
{% endfor %} |
|||
</Itens> |
|||
</RPS> |
|||
{% endfor %} |
|||
</Lote> |
|||
</ns1:ReqEnvioLoteRPS> |
|||
@ -0,0 +1,12 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" |
|||
xmlns:dsf="http://dsfnet.com.br"> |
|||
<soapenv:Body> |
|||
<dsf:enviar soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> |
|||
<mensagemXml xsi:type="xsd:string"><![CDATA[ |
|||
{% block content %}{% endblock %} |
|||
]]></mensagemXml> |
|||
</dsf:enviar> |
|||
</soapenv:Body> |
|||
</soapenv:Envelope> |
|||
@ -0,0 +1,119 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2017 Danimar Ribeiro, Trustcode |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import os |
|||
import hashlib |
|||
import base64 |
|||
import requests |
|||
from pytrustnfe.xml import render_xml, sanitize_response |
|||
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key |
|||
from pytrustnfe.nfse.assinatura import Assinatura |
|||
|
|||
URLS = { |
|||
'producao': { |
|||
'processar_nota': 'https://nfps-e.pmf.sc.gov.br/api/v1/processamento/notas/processa', |
|||
'cancelar_nota': 'https://nfps-e.pmf.sc.gov.br/api/v1/cancelamento/notas/cancela' |
|||
}, |
|||
'homologacao': { |
|||
'processar_nota': 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/processamento/notas/processa', |
|||
'cancelar_nota': 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/cancelamento/notas/cancela' |
|||
} |
|||
} |
|||
|
|||
|
|||
def _render(certificado, method, **kwargs): |
|||
path = os.path.join(os.path.dirname(__file__), 'templates') |
|||
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs) |
|||
|
|||
cert, key = extract_cert_and_key_from_pfx( |
|||
certificado.pfx, certificado.password) |
|||
cert, key = save_cert_key(cert, key) |
|||
signer = Assinatura(cert, key, certificado.password) |
|||
xml_send = signer.assina_xml(xml_send, '') |
|||
return xml_send |
|||
|
|||
|
|||
def _get_oauth_token(**kwargs): |
|||
if kwargs['ambiente'] == 'producao': |
|||
url = 'https://nfps-e.pmf.sc.gov.br/api/v1/autenticacao/oauth/token' |
|||
else: |
|||
url = 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/autenticacao/oauth/token' |
|||
|
|||
m = hashlib.md5() |
|||
secret = "%s:%s" % (kwargs["client_id"], kwargs["secret_id"]) |
|||
auth = base64.b64encode(secret.encode('utf-8')) |
|||
headers = { |
|||
"Content-Type": "application/x-www-form-urlencoded", |
|||
"Authorization": "Basic %s" % auth.decode('utf-8').replace('\n', '') |
|||
} |
|||
m.update(kwargs["password"].encode('utf-8')) |
|||
password = m.hexdigest().upper() |
|||
|
|||
dados = "grant_type=password&username=%s&password=%s&client_id=%s&client_secret=%s" % ( |
|||
kwargs["username"], password, kwargs["client_id"], kwargs["secret_id"]) |
|||
r = requests.post(url, data=dados, headers=headers) |
|||
if r.status_code == 200: |
|||
return r.json() |
|||
else: |
|||
return r.json() |
|||
|
|||
|
|||
def _send(certificado, method, **kwargs): |
|||
url = URLS[kwargs['ambiente']][method] |
|||
xml_send = kwargs['xml'] |
|||
|
|||
token = _get_oauth_token(**kwargs) |
|||
if "access_token" not in token: |
|||
raise Exception("%s - %s: %s" % (token["status"], token["error"], |
|||
token["message"])) |
|||
kwargs.update({"numero": 1, 'access_token': token["access_token"]}) |
|||
|
|||
headers = {"Accept": "application/xml;charset=UTF-8", |
|||
"Content-Type": "application/xml", |
|||
"Authorization": "Bearer %s" % kwargs['access_token']} |
|||
r = requests.post(url, headers=headers, data=xml_send) |
|||
|
|||
response, obj = sanitize_response(r.text.strip().encode('utf-8')) |
|||
return { |
|||
'sent_xml': xml_send, |
|||
'received_xml': response, |
|||
'object': obj, |
|||
'status_code': r.status_code, |
|||
} |
|||
|
|||
|
|||
def xml_processar_nota(certificado, **kwargs): |
|||
return _render(certificado, 'processar_nota', **kwargs) |
|||
|
|||
|
|||
def processar_nota(certificado, **kwargs): |
|||
if "xml" not in kwargs: |
|||
kwargs['xml'] = xml_processar_nota(certificado, **kwargs) |
|||
return _send(certificado, 'processar_nota', **kwargs) |
|||
|
|||
|
|||
def xml_cancelar_nota(certificado, **kwargs): |
|||
return _render(certificado, 'cancelar_nota', **kwargs) |
|||
|
|||
|
|||
def cancelar_nota(certificado, **kwargs): |
|||
if "xml" not in kwargs: |
|||
kwargs['xml'] = xml_cancelar_nota(certificado, **kwargs) |
|||
return _send(certificado, 'cancelar_nota', **kwargs) |
|||
|
|||
|
|||
def consultar_nota(certificado, **kwargs): |
|||
if kwargs['ambiente'] == 'producao': |
|||
url = "https://nfps-e.pmf.sc.gov.br/api/v1/consultas/notas/numero/%s" % (kwargs["numero"]) |
|||
else: |
|||
url = "https://nfps-e-hml.pmf.sc.gov.br/api/v1/consultas/notas/numero/%s" % (kwargs["numero"]) |
|||
|
|||
headers = {"Accept": "application/json", |
|||
"Authorization": "Bearer %s" % kwargs['access_token']} |
|||
r = requests.get(url, headers=headers) |
|||
print(r.status_code) |
|||
if r.status_code == 200: |
|||
return r.text |
|||
else: |
|||
return r.text |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0"?> |
|||
<xmlCancelamentoNfpse> |
|||
<motivoCancelamento>{{ cancelamento.motivo }}</motivoCancelamento> |
|||
<nuAedf>{{ cancelamento.aedf }}</nuAedf> |
|||
<nuNotaFiscal>{{ cancelamento.numero }}</nuNotaFiscal> |
|||
<codigoVerificacao>{{ cancelamento.codigo_verificacao }}</codigoVerificacao> |
|||
</xmlCancelamentoNfpse> |
|||
@ -0,0 +1,41 @@ |
|||
<?xml version="1.0"?> |
|||
<xmlProcessamentoNfpse> |
|||
<bairroTomador>{{ rps.tomador.bairro|normalize|escape }}</bairroTomador> |
|||
<baseCalculo>{{ rps.base_calculo }}</baseCalculo> |
|||
<baseCalculoSubstituicao>0.0</baseCalculoSubstituicao> |
|||
<cfps>{{ rps.cfps }}</cfps> |
|||
<codigoMunicipioTomador>{{ rps.tomador.cidade }}</codigoMunicipioTomador> |
|||
<codigoPostalTomador>{{ rps.tomador.cep }}</codigoPostalTomador> |
|||
<complementoEnderecoTomador>{{ rps.tomador.complemento|normalize|escape }}</complementoEnderecoTomador> |
|||
<dadosAdicionais>{{ rps.observacoes|normalize|escape }}</dadosAdicionais> |
|||
<dataEmissao>{{ rps.data_emissao }}</dataEmissao> |
|||
<emailTomador>{{ rps.tomador.email }}</emailTomador> |
|||
<identificacao>{{ rps.numero }}</identificacao> |
|||
<identificacaoTomador>{{ rps.tomador.cnpj_cpf }}</identificacaoTomador> |
|||
<inscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</inscricaoMunicipalTomador> |
|||
<itensServico> |
|||
{% for item in rps.itens_servico -%} |
|||
<itemServico> |
|||
<aliquota>{{ item.aliquota }}</aliquota> |
|||
<baseCalculo>{{ item.base_calculo }}</baseCalculo> |
|||
<cst>{{ item.cst_servico }}</cst> |
|||
<descricaoServico>{{ item.name|normalize|escape }}</descricaoServico> |
|||
<idCNAE>{{ item.cnae }}</idCNAE> |
|||
<quantidade>{{ item.quantidade }}</quantidade> |
|||
<valorTotal>{{ item.valor_total }}</valorTotal> |
|||
<valorUnitario>{{ item.valor_unitario }}</valorUnitario> |
|||
</itemServico> |
|||
{% endfor %} |
|||
</itensServico> |
|||
<logradouroTomador>{{ rps.tomador.logradouro|normalize|escape }}</logradouroTomador> |
|||
<nomeMunicipioTomador></nomeMunicipioTomador> |
|||
<numeroAEDF>{{ rps.aedf }}</numeroAEDF> |
|||
<numeroEnderecoTomador>{{ rps.tomador.numero }}</numeroEnderecoTomador> |
|||
<paisTomador>1058</paisTomador> |
|||
<razaoSocialTomador>{{ rps.tomador.razao_social|normalize|escape }}</razaoSocialTomador> |
|||
<telefoneTomador>{{ rps.tomador.telefone }}</telefoneTomador> |
|||
<ufTomador>{{ rps.tomador.uf }}</ufTomador> |
|||
<valorISSQN>{{rps.valor_issqn }}</valorISSQN> |
|||
<valorISSQNSubstituicao>0.0</valorISSQNSubstituicao> |
|||
<valorTotalServicos>{{ rps.valor_total }}</valorTotalServicos> |
|||
</xmlProcessamentoNfpse> |
|||
@ -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> |
|||
@ -1 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?><RetornoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho Versao="1" xmlns=""><Sucesso>true</Sucesso><InformacoesLote><NumeroLote>2654364</NumeroLote><InscricaoPrestador>51212</InscricaoPrestador><CPFCNPJRemetente><CNPJ>21332900163</CNPJ></CPFCNPJRemetente><DataEnvioLote>2016-08-29T10:52:15</DataEnvioLote><QtdNotasProcessadas>1</QtdNotasProcessadas><TempoProcessamento>0</TempoProcessamento><ValorTotalServicos>1.35</ValorTotalServicos></InformacoesLote></Cabecalho><ChaveNFeRPS xmlns=""><ChaveNFe><InscricaoPrestador>52382</InscricaoPrestador><NumeroNFe>446</NumeroNFe><CodigoVerificacao>APR9MJR</CodigoVerificacao></ChaveNFe><ChaveRPS><InscricaoPrestador>51282</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>6</NumeroRPS></ChaveRPS></ChaveNFeRPS></RetornoEnvioLoteRPS> |
|||
@ -1,16 +1,18 @@ |
|||
lxml >= 3.5.0, < 4 |
|||
nose |
|||
mock |
|||
lxml >= 3.5.0, < 5 |
|||
coveralls |
|||
http://xmlsoft.org/sources/python/libxml2-python-2.6.21.tar.gz |
|||
https://github.com/odoo-brazil/pyxmlsec/archive/master.zip |
|||
Jinja2 |
|||
signxml |
|||
suds >= 0.4 |
|||
suds_requests >= 0.3 |
|||
defusedxml >= 0.4.1, < 0.6 |
|||
eight >= 0.3.0, < 0.5 |
|||
cryptography >= 1.8, < 1.10 |
|||
pyOpenSSL >= 16.0.0, < 17 |
|||
urllib3 >= 1.22 |
|||
suds-jurko >= 0.6 |
|||
suds-jurko-requests >= 1.1 |
|||
defusedxml >= 0.4.1, < 1 |
|||
eight >= 0.3.0, < 1 |
|||
cryptography >= 1.8, < 3 |
|||
pyOpenSSL >= 16.0.0, < 18 |
|||
certifi >= 2015.11.20.1 |
|||
xmlsec >= 1.3.3 |
|||
reportlab |
|||
pytest |
|||
pytest-cov |
|||
pytz |
|||
zeep |
|||
@ -1,4 +1,3 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<RetornoCancelamentoNFe |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
|||
@ -1,4 +1,3 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<RetornoCancelamentoNFe |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
|||
@ -0,0 +1 @@ |
|||
<RetornoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho Versao="1" xmlns=""><Sucesso>true</Sucesso><InformacoesLote><NumeroLote>2654364</NumeroLote><InscricaoPrestador>51212</InscricaoPrestador><CPFCNPJRemetente><CNPJ>21332900163</CNPJ></CPFCNPJRemetente><DataEnvioLote>2016-08-29T10:52:15</DataEnvioLote><QtdNotasProcessadas>1</QtdNotasProcessadas><TempoProcessamento>0</TempoProcessamento><ValorTotalServicos>1.35</ValorTotalServicos></InformacoesLote></Cabecalho><ChaveNFeRPS xmlns=""><ChaveNFe><InscricaoPrestador>52382</InscricaoPrestador><NumeroNFe>446</NumeroNFe><CodigoVerificacao>APR9MJR</CodigoVerificacao></ChaveNFe><ChaveRPS><InscricaoPrestador>51282</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>6</NumeroRPS></ChaveRPS></ChaveNFeRPS></RetornoEnvioLoteRPS> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue