11 changed files with 899 additions and 0 deletions
-
107pytrustnfe/cte/__init__.py
-
459pytrustnfe/cte/dacte.py
-
0pytrustnfe/cte/templates/CTeDistribuicaoDFe.xml
-
0pytrustnfe/cte/templates/CTeRecepcaoOS.xml
-
0pytrustnfe/cte/templates/CteConsultaCadastro.xml
-
15pytrustnfe/cte/templates/CteConsultaProtocolo.xml
-
10pytrustnfe/cte/templates/CteInutilizacao.xml
-
305pytrustnfe/cte/templates/CteRecepcao.xml
-
0pytrustnfe/cte/templates/CteRecepcaoEvento.xml
-
0pytrustnfe/cte/templates/CteRetRecepcao.xml
-
3pytrustnfe/cte/webservices.py
@ -0,0 +1,107 @@ |
|||||
|
import os |
||||
|
from lxml import etree |
||||
|
from pytrustnfe.nfe.assinatura import Assinatura |
||||
|
from pytrustnfe.xml import render_xml, sanitize_response |
||||
|
from pytrustnfe.cte.webservices import localizar_url |
||||
|
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key |
||||
|
|
||||
|
# Zeep |
||||
|
from requests import Session |
||||
|
from zeep import Client |
||||
|
from zeep.transports import Transport |
||||
|
|
||||
|
|
||||
|
def _render(certificado, method, sign, reference, **kwargs): |
||||
|
path = os.path.join(os.path.dirname(__file__), 'templates') |
||||
|
xmlElem_send = render_xml(path, '%s.xml' % method, True, **kwargs) |
||||
|
|
||||
|
if sign: |
||||
|
signer = Assinatura(certificado.pfx, certificado.password) |
||||
|
xml_send = signer.assina_xml(xmlElem_send, reference) |
||||
|
else: |
||||
|
xml_send = etree.tostring(xmlElem_send, encoding=str) |
||||
|
return xml_send |
||||
|
|
||||
|
|
||||
|
def _send(certificado, method, **kwargs): |
||||
|
xml_send = kwargs["xml"] |
||||
|
base_url = localizar_url(method, kwargs['estado'], kwargs['ambiente']) |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
parser = etree.XMLParser(strip_cdata=False) |
||||
|
xml = etree.fromstring(xml_send, parser=parser) |
||||
|
|
||||
|
client = Client(base_url, transport=transport) |
||||
|
|
||||
|
port = next(iter(client.wsdl.port_types)) |
||||
|
first_operation = [x for x in iter( |
||||
|
client.wsdl.port_types[port].operations) if "zip" not in x.lower()][0] |
||||
|
|
||||
|
namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe") |
||||
|
if namespaceNFe is not None: |
||||
|
namespaceNFe.set('xmlns', 'http://www.portalfiscal.inf.br/nfe') |
||||
|
|
||||
|
with client.settings(raw_response=True): |
||||
|
response = client.service[first_operation](xml) |
||||
|
response, obj = sanitize_response(response.text) |
||||
|
return { |
||||
|
'sent_xml': xml_send, |
||||
|
'received_xml': response, |
||||
|
'object': obj.Body.getchildren()[0] |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def cte_recepcao(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteRecepcao', True, **kwargs) |
||||
|
return _send(certificado, 'CteRecepcao', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_retorno_recepcao(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteRetRecepcao', True, **kwargs) |
||||
|
return _send(certificado, 'CteRetRecepcao', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_inutilizacao(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteInutilizacao', True, **kwargs) |
||||
|
return _send(certificado, 'CteInutilizacao', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_consulta_protocolo(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteConsultaProtocolo', True, **kwargs) |
||||
|
return _send(certificado, 'CteConsultaProtocolo', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_consulta_cadastro(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteConsultaCadastro', True, **kwargs) |
||||
|
return _send(certificado, 'CteConsultaCadastro', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_recepcao_evento(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CteRecepcaoEvento', True, **kwargs) |
||||
|
return _send(certificado, 'CteRecepcaoEvento', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_recepcao_os(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CTeRecepcaoOS', True, **kwargs) |
||||
|
return _send(certificado, 'CTeRecepcaoOS', **kwargs) |
||||
|
|
||||
|
|
||||
|
def cte_distribuicao_dfe(certificado, gerar_xml, **kwargs): # Assinar |
||||
|
if gerar_xml: |
||||
|
return _render(certificado, 'CTeDistribuicaoDFe', True, **kwargs) |
||||
|
return _send(certificado, 'CTeDistribuicaoDFe', **kwargs) |
||||
@ -0,0 +1,459 @@ |
|||||
|
# © 2018 Danimar Ribeiro <danimaribeiro@gmail.com> |
||||
|
# 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, timezone=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): |
||||
|
payment = [] |
||||
|
tipo_pagamento = tagtext(oNode=item, cTag="tPag") |
||||
|
val = format_number(tagtext(oNode=item, cTag="vPag"), precision=2) |
||||
|
|
||||
|
method = payment_method_list[tipo_pagamento] |
||||
|
|
||||
|
payment.append(method) |
||||
|
payment.append(val) |
||||
|
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,15 @@ |
|||||
|
<Consulta> |
||||
|
<ModeloDocumento>CTe</ModeloDocumento> |
||||
|
<Versao>3.00</Versao> |
||||
|
<tpAmb>2</tpAmb> |
||||
|
<CnpjEmpresa>99999999999999</CnpjEmpresa> |
||||
|
<CnpjEmissor>99999999999999</CnpjEmissor> |
||||
|
<NumeroInicial>1</NumeroInicial> |
||||
|
<NumeroFinal>10</NumeroFinal> |
||||
|
<Serie>1</Serie> |
||||
|
<ChaveAcesso/> |
||||
|
<DataEmissaoInicial/> |
||||
|
<DataEmissaoFinal/> |
||||
|
<dhUF/> |
||||
|
<EmitidoRecebido/> |
||||
|
</Consulta> |
||||
@ -0,0 +1,10 @@ |
|||||
|
<Inutilizacao> |
||||
|
<ModeloDocumento>CTe</ModeloDocumento> |
||||
|
<Versao>3.00</Versao> |
||||
|
<CnpjEmissor>99999999999999</CnpjEmissor> |
||||
|
<tpAmb>2</tpAmb> |
||||
|
<NumeroInicial>1</NumeroInicial> |
||||
|
<NumeroFinal>1</NumeroFinal> |
||||
|
<Serie>50</Serie> |
||||
|
<Justificativa>Inutilizacao por motivos de pulo de numeracao</Justificativa> |
||||
|
</Inutilizacao> |
||||
@ -0,0 +1,305 @@ |
|||||
|
<Envio> |
||||
|
<ModeloDocumento>CTe</ModeloDocumento> |
||||
|
<Versao>3.00</Versao> |
||||
|
<ide> |
||||
|
<cCT>17281638</cCT> |
||||
|
<cUF>43</cUF> |
||||
|
<natOp>Frete de mercadorias</natOp> |
||||
|
<CFOP>5352</CFOP> |
||||
|
<mod>57</mod> |
||||
|
<serie>50</serie> |
||||
|
<nCT>14</nCT> |
||||
|
<dhEmi>2017-06-06T15:00:01</dhEmi> |
||||
|
<fusoHorario>-03:00</fusoHorario> |
||||
|
<tpImp>1</tpImp> |
||||
|
<tpEmis>1</tpEmis> |
||||
|
<tpAmb>2</tpAmb> |
||||
|
<tpCTe>0</tpCTe> |
||||
|
<procEmi>0</procEmi> |
||||
|
<cMunEnv>4310405</cMunEnv> |
||||
|
<xMunEnv>Independencia</xMunEnv> |
||||
|
<UFEnv>RS</UFEnv> |
||||
|
<modal>01</modal> |
||||
|
<tpServ>0</tpServ> |
||||
|
<cMunIni>4310405</cMunIni> |
||||
|
<xMunIni>Independencia</xMunIni> |
||||
|
<UFIni>RS</UFIni> |
||||
|
<cMunFim>4302204</cMunFim> |
||||
|
<xMunFim>Boa Vista do Burica</xMunFim> |
||||
|
<UFFim>RS</UFFim> |
||||
|
<retira>0</retira> |
||||
|
<xDetRetira>Retirar o pacote no local de entrega indicado</xDetRetira> |
||||
|
<indIEToma>9</indIEToma> |
||||
|
<tomador> |
||||
|
<toma>4</toma> |
||||
|
<CNPJ_toma/> |
||||
|
<CPF_toma>99999999999</CPF_toma> |
||||
|
<IE_toma/> |
||||
|
<xNome_toma>Ricardo</xNome_toma> |
||||
|
<xFant_toma/> |
||||
|
<fone_toma>5597207426</fone_toma> |
||||
|
<enderTomador> |
||||
|
<xLgr_toma>Rua Senador Salgado Filho</xLgr_toma> |
||||
|
<nro_toma>463</nro_toma> |
||||
|
<xCpl_toma>Complemento nao informado</xCpl_toma> |
||||
|
<xBairro_toma>Centro</xBairro_toma> |
||||
|
<cMun_toma>4310405</cMun_toma> |
||||
|
<xMun_toma>Independencia</xMun_toma> |
||||
|
<CEP_toma>98915000</CEP_toma> |
||||
|
<UF_toma>RS</UF_toma> |
||||
|
<cPais_toma>1046</cPais_toma> |
||||
|
<xPais_toma>Brasil</xPais_toma> |
||||
|
</enderTomador> |
||||
|
<email_toma>email@gmail.com.br</email_toma> |
||||
|
</tomador> |
||||
|
</ide> |
||||
|
<compl> |
||||
|
<xCaracAd>Encomenda</xCaracAd> |
||||
|
<xCaracSer>Transporte</xCaracSer> |
||||
|
<xEmi>Jose Campos</xEmi> |
||||
|
<xObs>Pagar em qualquer lugar</xObs> |
||||
|
<fluxo> |
||||
|
<xOrig>Angra dos Reis</xOrig> |
||||
|
<xDest>Maria da Silva</xDest> |
||||
|
<xRota>129386</xRota> |
||||
|
<passagem> |
||||
|
<passItem> |
||||
|
<xPass>LHR</xPass> |
||||
|
</passItem> |
||||
|
<passItem> |
||||
|
<xPass>CGH</xPass> |
||||
|
</passItem> |
||||
|
<passItem> |
||||
|
<xPass>POA</xPass> |
||||
|
</passItem> |
||||
|
</passagem> |
||||
|
</fluxo> |
||||
|
<Entrega> |
||||
|
<tpPer>1</tpPer> |
||||
|
<dProg>2014-07-03</dProg> |
||||
|
<dIni>2014-07-03</dIni> |
||||
|
<dFim>2014-07-03</dFim> |
||||
|
<tpHor>1</tpHor> |
||||
|
<hProg>10:30:22</hProg> |
||||
|
<hIni>10:30:22</hIni> |
||||
|
<hFim>10:30:22</hFim> |
||||
|
</Entrega> |
||||
|
</compl> |
||||
|
<emit> |
||||
|
<CNPJ_emit>99999999999999</CNPJ_emit> |
||||
|
<IE>9999999999</IE> |
||||
|
<IEST>999999999</IEST> |
||||
|
<xNome>CT-E EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xNome> |
||||
|
<xFant>EMPRESA 1</xFant> |
||||
|
<enderEmit> |
||||
|
<xLgr>Endereco</xLgr> |
||||
|
<nro>1234</nro> |
||||
|
<xCpl>Complemento</xCpl> |
||||
|
<xBairro>Centro</xBairro> |
||||
|
<cMun>4310405</cMun> |
||||
|
<xMun>Independencia</xMun> |
||||
|
<CEP>98910000</CEP> |
||||
|
<UF>RS</UF> |
||||
|
<fone>35354560</fone> |
||||
|
</enderEmit> |
||||
|
</emit> |
||||
|
<rem> |
||||
|
<CNPJ_rem/> |
||||
|
<CPF_rem>99999999999</CPF_rem> |
||||
|
<IE_rem/> |
||||
|
<xNome_rem>CT-E EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xNome_rem> |
||||
|
<xFant_rem>Migrate Company</xFant_rem> |
||||
|
<fone_rem>55118822773343</fone_rem> |
||||
|
<email_rem>email@gmail.com.br</email_rem> |
||||
|
<enderRem> |
||||
|
<xLgr_rem>Rua Angra dos Reis</xLgr_rem> |
||||
|
<nro_rem>666</nro_rem> |
||||
|
<xCpl_rem/> |
||||
|
<xBairro_rem>Centro</xBairro_rem> |
||||
|
<cMun_rem>4310405</cMun_rem> |
||||
|
<xMun_rem>Independencia</xMun_rem> |
||||
|
<CEP_rem>98917666</CEP_rem> |
||||
|
<UF_rem>RS</UF_rem> |
||||
|
<cPais_rem>1046</cPais_rem> |
||||
|
<xPais_rem>Brasil</xPais_rem> |
||||
|
</enderRem> |
||||
|
</rem> |
||||
|
<exped> |
||||
|
<CNPJ_exp>99999999999999</CNPJ_exp> |
||||
|
<IE_exp>9999999999</IE_exp> |
||||
|
<xNome_exp>CT-E EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xNome_exp> |
||||
|
<fone_exp>5535358668</fone_exp> |
||||
|
<email_exp>email@email.com.br</email_exp> |
||||
|
<enderExped> |
||||
|
<xLgr_exp>Rua das Palmeiras</xLgr_exp> |
||||
|
<nro_exp>115</nro_exp> |
||||
|
<xCpl_exp>Próximo ao bar do Zé</xCpl_exp> |
||||
|
<xBairro_exp>Centro</xBairro_exp> |
||||
|
<cMun_exp>4310405</cMun_exp> |
||||
|
<xMun_exp>Independencia</xMun_exp> |
||||
|
<CEP_exp>98915000</CEP_exp> |
||||
|
<UF_exp>RS</UF_exp> |
||||
|
<cPais_exp>1046</cPais_exp> |
||||
|
<xPais_exp>Brasil</xPais_exp> |
||||
|
</enderExped> |
||||
|
</exped> |
||||
|
<receb> |
||||
|
<CNPJ_rec/> |
||||
|
<CPF_rec>99999999999</CPF_rec> |
||||
|
<IE_rec/> |
||||
|
<xNome_rec>CT-E EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xNome_rec> |
||||
|
<fone_rec>5597207496</fone_rec> |
||||
|
<email_rec>email@gmail.com.br</email_rec> |
||||
|
<enderReceb> |
||||
|
<xLgr_rec>Rua Senador Salgado Filho</xLgr_rec> |
||||
|
<nro_rec>463</nro_rec> |
||||
|
<xCpl_rec/> |
||||
|
<xBairro_rec>Centro</xBairro_rec> |
||||
|
<cMun_rec>4310405</cMun_rec> |
||||
|
<xMun_rec>Independencia</xMun_rec> |
||||
|
<CEP_rec>98915000</CEP_rec> |
||||
|
<UF_rec>RS</UF_rec> |
||||
|
<cPais_rec>1046</cPais_rec> |
||||
|
<xPais_rec>Brasil</xPais_rec> |
||||
|
</enderReceb> |
||||
|
</receb> |
||||
|
<dest> |
||||
|
<CPF_dest>99999999999</CPF_dest> |
||||
|
<IE_dest /> |
||||
|
<xNome_dest>CT-E EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xNome_dest> |
||||
|
<fone_dest>5599733466</fone_dest> |
||||
|
<email_dest>email@gmail.com.br</email_dest> |
||||
|
<enderDest> |
||||
|
<xLgr_dest>Rua Travessa</xLgr_dest> |
||||
|
<nro_dest>280</nro_dest> |
||||
|
<xCpl_dest>Próximo ao hospital</xCpl_dest> |
||||
|
<xBairro_dest>Rita</xBairro_dest> |
||||
|
<cMun_dest>4310405</cMun_dest> |
||||
|
<xMun_dest>Independencia</xMun_dest> |
||||
|
<CEP_dest>98910000</CEP_dest> |
||||
|
<UF_dest>RS</UF_dest> |
||||
|
<cPais_dest>1046</cPais_dest> |
||||
|
<xPais_dest>Brasil</xPais_dest> |
||||
|
</enderDest> |
||||
|
</dest> |
||||
|
<vPrest> |
||||
|
<vTPrest>32321.23</vTPrest> |
||||
|
<vRec>12312.23</vRec> |
||||
|
<Comp> |
||||
|
<compItem> |
||||
|
<xNome_comp>Componente 1</xNome_comp> |
||||
|
<vComp>812.44</vComp> |
||||
|
</compItem> |
||||
|
<compItem> |
||||
|
<xNome_comp>Componente 2</xNome_comp> |
||||
|
<vComp>1612.44</vComp> |
||||
|
</compItem> |
||||
|
</Comp> |
||||
|
</vPrest> |
||||
|
<imp> |
||||
|
<vTotImp>59.90</vTotImp> |
||||
|
<infAdFisco>Informacoes de interesse do fisco estarão escritas aqui</infAdFisco> |
||||
|
<ICMS> |
||||
|
<CST>00</CST> |
||||
|
<vBC>12</vBC> |
||||
|
<pICMS>13</pICMS> |
||||
|
<vICMS>1.56</vICMS> |
||||
|
<indSN>1</indSN> |
||||
|
</ICMS> |
||||
|
</imp> |
||||
|
<infCTeNorm> |
||||
|
<infCarga> |
||||
|
<vMerc>599.65</vMerc> |
||||
|
<proPred>Pepitas de ouro</proPred> |
||||
|
<xOutCat>Que valem mais do que dinheiro</xOutCat> |
||||
|
<infQ> |
||||
|
<infQItem> |
||||
|
<cUnid>01</cUnid> |
||||
|
<tpMed>PESO BASE DE CÁLCULO</tpMed> |
||||
|
<qCarga>899</qCarga> |
||||
|
</infQItem> |
||||
|
<infQItem> |
||||
|
<cUnid>01</cUnid> |
||||
|
<tpMed>PESO BRUTO</tpMed> |
||||
|
<qCarga>500</qCarga> |
||||
|
</infQItem> |
||||
|
<infQItem> |
||||
|
<cUnid>00</cUnid> |
||||
|
<tpMed>M3</tpMed> |
||||
|
<qCarga>600</qCarga> |
||||
|
</infQItem> |
||||
|
<infQItem> |
||||
|
<cUnid>03</cUnid> |
||||
|
<tpMed>UNIDADE</tpMed> |
||||
|
<qCarga>520</qCarga> |
||||
|
</infQItem> |
||||
|
<infQItem> |
||||
|
<cUnid>05</cUnid> |
||||
|
<tpMed>PESO AFERIDO</tpMed> |
||||
|
<qCarga>332</qCarga> |
||||
|
</infQItem> |
||||
|
</infQ> |
||||
|
<vCargaAverb>200.00</vCargaAverb> |
||||
|
</infCarga> |
||||
|
<infDoc> |
||||
|
<infDocItem> |
||||
|
<tipoDocumento>1</tipoDocumento> |
||||
|
<mod_nf>01</mod_nf> |
||||
|
<serie_nf>445</serie_nf> |
||||
|
<nDoc_nf>887</nDoc_nf> |
||||
|
<dEmi_nf>2014-09-03</dEmi_nf> |
||||
|
<vBC_nf>99.01</vBC_nf> |
||||
|
<vICMS_nf>99.02</vICMS_nf> |
||||
|
<vBCST_nf>99.03</vBCST_nf> |
||||
|
<vST_nf>99.04</vST_nf> |
||||
|
<vProd_nf>99.05</vProd_nf> |
||||
|
<vNF_nf>99.06</vNF_nf> |
||||
|
<CFOP_nf>5352</CFOP_nf> |
||||
|
<UnidadesTransp> |
||||
|
<UnidadesTranspItem> |
||||
|
<tipoUnidTransporte>5</tipoUnidTransporte> |
||||
|
<idUnidTransporte>BCIASUDX988</idUnidTransporte> |
||||
|
<qtdRateada>999</qtdRateada> |
||||
|
<LacresUnidTransp> |
||||
|
<LacresUnidTranspItem> |
||||
|
<nLacreUnidTransp>999</nLacreUnidTransp> |
||||
|
</LacresUnidTranspItem> |
||||
|
</LacresUnidTransp> |
||||
|
<UnidadesCargaTran> |
||||
|
<UnidadesCargaTranItem> |
||||
|
<tipoUnidCargaTran>2</tipoUnidCargaTran> |
||||
|
<idUnidCargaTran>22</idUnidCargaTran> |
||||
|
<qtdRateadaCargaTran>10</qtdRateadaCargaTran> |
||||
|
<LacresUnidCargaTran> |
||||
|
<LacresUnidCargaTranItem> |
||||
|
<nLacreUnidCargaTran>100</nLacreUnidCargaTran> |
||||
|
</LacresUnidCargaTranItem> |
||||
|
</LacresUnidCargaTran> |
||||
|
</UnidadesCargaTranItem> |
||||
|
</UnidadesCargaTran> |
||||
|
</UnidadesTranspItem> |
||||
|
</UnidadesTransp> |
||||
|
</infDocItem> |
||||
|
</infDoc> |
||||
|
<infModal> |
||||
|
<versaoModal>3.00</versaoModal> |
||||
|
<rodo> |
||||
|
<RNTRC>89173646</RNTRC> |
||||
|
<dPrev_rodo>2017-05-19</dPrev_rodo> |
||||
|
<occ> |
||||
|
<occItem> |
||||
|
<serie_occ>123</serie_occ> |
||||
|
<nOcc>123456</nOcc> |
||||
|
<dEmi_occ>2017-05-19</dEmi_occ> |
||||
|
<emiOcc> |
||||
|
<CNPJ_occ>99999999999999</CNPJ_occ> |
||||
|
<cInt_occ>123</cInt_occ> |
||||
|
<IE_occ>9999999999</IE_occ> |
||||
|
<UF_occ>RS</UF_occ> |
||||
|
</emiOcc> |
||||
|
</occItem> |
||||
|
</occ> |
||||
|
</rodo> |
||||
|
</infModal> |
||||
|
</infCTeNorm> |
||||
|
</Envio> |
||||
@ -0,0 +1,3 @@ |
|||||
|
|
||||
|
|
||||
|
def localizar_url(servico, estado, ambiente=2): |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue