From 8b8c4b00091c4d97a945a87d545ab05c64483c18 Mon Sep 17 00:00:00 2001 From: edsonitk Date: Mon, 13 Feb 2017 10:32:10 -0200 Subject: [PATCH 1/2] =?UTF-8?q?Impress=C3=A3o=20de=20danfe=20atrav=C3=A9s?= =?UTF-8?q?=20do=20reportlab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfe/danfe.py | 811 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 811 insertions(+) create mode 100644 pytrustnfe/nfe/danfe.py diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py new file mode 100644 index 0000000..295cc64 --- /dev/null +++ b/pytrustnfe/nfe/danfe.py @@ -0,0 +1,811 @@ +# -*- coding: utf-8 -*- +# © 2017 Edson Bernardino, ITK Soft +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# Classe para geração de PDF da DANFE a partir de xml etree.fromstring + +# TO DO +# Layout Paisagem +# Logo imagem da empresa + +from cStringIO import StringIO as IO +from lxml import etree +from textwrap import wrap + +from reportlab.lib import utils +from reportlab.pdfgen import canvas +from reportlab.lib.units import mm, cm +from reportlab.lib.pagesizes import A4 +from reportlab.lib.colors import black, gray +from reportlab.graphics.barcode import code128 +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.platypus import Paragraph, Image + + +def chunks(cString, nLen): + for start in range(0, len(cString), nLen): + yield cString[start:start+nLen] + + +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)) + + +class danfe(object): + def __init__(self, sizepage=A4, list_xml=None, recibo=True, + orientation='portrait'): + self.width = 210 # 21 x 29,7cm + self.height = 297 + self.nLeft = 10 + self.nRight = 10 + self.nTop = 7 + self.nBottom = 15 + self.nlin = self.nTop + self.oFrete = {'0': '0 - Emitente', + '1': '1 - Dest/Remet', + '2': '2 - Terceiros', + '9': '9 - Sem Frete'} + + self.oPDF_IO = IO() + if orientation == 'landscape': + raise NameError('Rotina não implementada') + else: + size = sizepage + + self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=size) + self.canvas.setTitle('DANFE') + self.canvas.setStrokeColor(black) + + for oXML in 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 29 + # [ 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 = [] + 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'), 51) + if infAdProd is not None: + list_.extend(wrap(infAdProd.text, 51)) + list_desc.append(list_) + + # 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 + + if recibo: + self.recibo_entrega(oXML=oXML) + + self.ide_emit(oXML=oXML) + self.destinatario(oXML=oXML) + + if oXML_cobr is not None: + self.faturas(oXML=oXML_cobr) + + self.impostos(oXML=oXML) + self.transportes(oXML=oXML) + self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0], + list_desc=list_desc) + + self.adicionais(oXML=oXML) + + # Gera o restante das páginas do XML + for oPag in oPaginator[1:]: + self.newpage() + self.ide_emit(oXML=oXML) + self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag, + list_desc=list_desc, nHeight=77) + + self.newpage() + + self.canvas.save() + + def ide_emit(self, oXML=None): + elem_infNFe = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}infNFe") + elem_protNFe = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}protNFe") + elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") + elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + + cChave = elem_infNFe.attrib.get('Id')[3:] + barcode128 = code128.Code128(cChave, barHeight=10*mm, barWidth=0.25*mm) + + self.canvas.setLineWidth(.5) + self.rect(self.nLeft, self.nlin+1, self.nLeft+75, 32) + self.rect(self.nLeft+115, self.nlin+1, + self.width-self.nLeft-self.nRight-115, 39) + + self.rect(self.nLeft+116, self.nlin+15, + self.width-self.nLeft-self.nRight-117, 6) + + self.rect(self.nLeft, self.nlin+33, + self.width-self.nLeft-self.nRight, 14) + self.hline(self.nLeft, self.nlin+40, self.width-self.nRight) + self.vline(self.nLeft+60, self.nlin+40, 7) + self.vline(self.nLeft+100, self.nlin+40, 7) + + # Labels + self.canvas.setFont('NimbusSanL-Bold', 12) + self.stringcenter(self.nLeft+98, self.nlin+4, 'DANFE') + self.stringcenter(self.nLeft+109, self.nlin+19.5, + tagtext(oNode=elem_ide, cTag='tpNF')) + self.canvas.setFont('NimbusSanL-Bold', 8) + cNF = tagtext(oNode=elem_ide, cTag='nNF') + cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") + self.stringcenter(self.nLeft+100, self.nlin+25, "Nº %s" % (cNF)) + + self.stringcenter(self.nLeft+100, self.nlin+29, u"SÉRIE %s" % ( + tagtext(oNode=elem_ide, cTag='serie'))) + cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) + self.stringcenter(self.nLeft+100, self.nlin+32, cPag) + self.canvas.setFont('NimbusSanL-Regu', 6) + self.string(self.nLeft+86, self.nlin+8, 'Documento Auxiliar da') + self.string(self.nLeft+86, self.nlin+10.5, 'Nota Fiscal Eletrônica') + self.string(self.nLeft+86, self.nlin+16, '0 - Entrada') + self.string(self.nLeft+86, self.nlin+19, '1 - Saída') + self.rect(self.nLeft+105, self.nlin+15, 8, 6) + + self.stringcenter( + self.nLeft+152, self.nlin+25, + 'Consulta de autenticidade no portal nacional da NF-e') + self.stringcenter( + self.nLeft+152, self.nlin+28, + 'www.nfe.fazenda.gov.br/portal ou no site da SEFAZ Autorizadora') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+117, self.nlin+16.7, 'CHAVE DE ACESSO') + self.string(self.nLeft+116, self.nlin+2.7, 'CONTROLE DO FISCO') + + self.string(self.nLeft+1, self.nlin+34.7, 'NATUREZA DA OPERAÇÃO') + self.string(self.nLeft+116, self.nlin+34.7, + 'PROTOCOLO DE AUTORIZAÇÃO DE USO') + self.string(self.nLeft+1, self.nlin+41.7, 'INSCRIÇÃO ESTADUAL') + self.string(self.nLeft+61, self.nlin+41.7, + 'INSCRIÇÃO ESTADUAL DO SUBST. TRIB.') + self.string(self.nLeft+101, self.nlin+41.7, 'CNPJ') + + # Conteúdo campos + barcode128.drawOn(self.canvas, (self.nLeft+111.5)*mm, + (self.height-self.nlin-14)*mm) + self.canvas.setFont('NimbusSanL-Bold', 6) + nW_Rect = (self.width-self.nLeft-self.nRight-117) / 2 + self.stringcenter(self.nLeft+116.5+nW_Rect, self.nlin+19.5, + ' '.join(chunks(cChave, 4))) # Chave + self.canvas.setFont('NimbusSanL-Regu', 8) + cDt, cHr = getdateUTC(tagtext(oNode=elem_protNFe, cTag='dhRecbto')) + cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt') + cDt = cProtocolo + ' - ' + cDt + ' ' + cHr + nW_Rect = (self.width-self.nLeft-self.nRight-110) / 2 + self.stringcenter(self.nLeft+115+nW_Rect, self.nlin+38.7, cDt) + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+38.7, + tagtext(oNode=elem_ide, cTag='natOp')) + self.string(self.nLeft+1, self.nlin+46, + tagtext(oNode=elem_emit, cTag='IE')) + self.string(self.nLeft+101, self.nlin+46, + format_cnpj_cpf(tagtext(oNode=elem_emit, cTag='CNPJ'))) + + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontSize = 11 + styleN.fontName = 'NimbusSanL-Regu' + + # Razão Social emitente + P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) + w, h = P.wrap(55*mm, 50*mm) + P.drawOn(self.canvas, (self.nLeft+30)*mm, + (self.height-self.nlin-10)*mm) + + logo = '/home/danimar/Downloads/innova.png' + img = get_image(logo, width=2*cm) + img.drawOn(self.canvas, (self.nLeft+5)*mm, (self.height-self.nlin-22)*mm) + + cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( + oNode=elem_emit, cTag='nro') + ' - ' + cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + ' - ' + tagtext( + oNode=elem_emit, cTag='xMun') + ' - ' + cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext( + oNode=elem_emit, cTag='CEP') + cEnd += ' - Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + styleN.fontSize = 8 + styleN.leading = 10 + P = Paragraph(cEnd, styleN) + w, h = P.wrap(80*mm, 30*mm) + P.drawOn(self.canvas, (self.nLeft+5)*mm, (self.height-self.nlin-30)*mm) + + # Homologação + if tagtext(oNode=elem_ide, cTag='tpAmb') == '2': + self.canvas.saveState() + self.canvas.rotate(90) + self.canvas.setFont('Times-Bold', 40) + self.canvas.setFillColorRGB(0.57, 0.57, 0.57) + self.string(self.nLeft+65, 449, 'SEM VALOR FISCAL') + self.canvas.restoreState() + + self.nlin += 48 + + def destinatario(self, oXML=None): + elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + elem_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") + nMr = self.width-self.nRight + + self.nlin += 1 + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'DESTINATÁRIO/REMETENTE') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 20) + self.vline(nMr-25, self.nlin+2, 20) + self.hline(self.nLeft, self.nlin+8.66, self.width-self.nLeft) + self.hline(self.nLeft, self.nlin+15.32, self.width-self.nLeft) + self.vline(nMr-70, self.nlin+2, 6.66) + self.vline(nMr-53, self.nlin+8.66, 6.66) + self.vline(nMr-99, self.nlin+8.66, 6.66) + self.vline(nMr-90, self.nlin+15.32, 6.66) + self.vline(nMr-102, self.nlin+15.32, 6.66) + self.vline(nMr-136, self.nlin+15.32, 6.66) + # Labels/Fields + self.canvas.setFont('NimbusSanL-Bold', 5) + self.string(self.nLeft+1, self.nlin+3.7, 'NOME/RAZÃO SOCIAL') + self.string(nMr-69, self.nlin+3.7, 'CNPJ/CPF') + self.string(nMr-24, self.nlin+3.7, 'DATA DA EMISSÃO') + self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') + self.string(nMr-98, self.nlin+10.3, 'BAIRRO/DISTRITO') + self.string(nMr-52, self.nlin+10.3, 'CEP') + self.string(nMr-24, self.nlin+10.3, 'DATA DE ENTRADA/SAÍDA') + self.string(self.nLeft+1, self.nlin+17.1, 'MUNICÍPIO') + self.string(nMr-135, self.nlin+17.1, 'FONE/FAX') + self.string(nMr-101, self.nlin+17.1, 'UF') + self.string(nMr-89, self.nlin+17.1, 'INSCRIÇÃO ESTADUAL') + self.string(nMr-24, self.nlin+17.1, 'HORA DE ENTRADA/SAÍDA') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+7.5, + tagtext(oNode=elem_dest, cTag='xNome')) + self.string(nMr-69, self.nlin+7.5, + format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CNPJ'))) + cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi')) + self.string(nMr-24, self.nlin+7.7, cDt) + cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt')) + self.string(nMr-24, self.nlin+14.3, cDt) # Dt saída + cEnd = tagtext(oNode=elem_dest, cTag='xLgr') + ', ' + tagtext( + oNode=elem_dest, cTag='nro') + self.string(self.nLeft+1, self.nlin+14.3, cEnd) + self.string(nMr-98, self.nlin+14.3, + tagtext(oNode=elem_dest, cTag='xBairro')) + self.string(nMr-52, self.nlin+14.3, + tagtext(oNode=elem_dest, cTag='CEP')) + self.string(self.nLeft+1, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='xMun')) + self.string(nMr-135, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='fone')) + self.string(nMr-101, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='UF')) + self.string(nMr-89, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='IE')) + + self.nlin += 24 # Nr linhas ocupadas pelo bloco + + def faturas(self, oXML=None): + + nMr = self.width-self.nRight + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'FATURA') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 13) + self.vline(nMr-47.5, self.nlin+2, 13) + self.vline(nMr-95, self.nlin+2, 13) + self.vline(nMr-142.5, self.nlin+2, 13) + self.hline(nMr-47.5, self.nlin+8.5, self.width-self.nLeft) + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(nMr-46.5, self.nlin+3.8, 'CÓDIGO VENDEDOR') + self.string(nMr-46.5, self.nlin+10.2, 'NOME VENDEDOR') + self.string(nMr-93.5, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + self.string(nMr-140.5, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + self.string(self.nLeft+2, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Bold', 6) + nLin = 7 + nPar = 1 + nCol = 0 + nAju = 0 + + line_iter = iter(oXML[1:10]) # Salta elemt 1 e considera os próximos 9 + for oXML_dup in line_iter: + + cDt, cHr = getdateUTC(tagtext(oNode=oXML_dup, cTag='dVenc')) + self.string(self.nLeft+nCol+1, self.nlin+nLin, + tagtext(oNode=oXML_dup, cTag='nDup')) + self.string(self.nLeft+nCol+17, self.nlin+nLin, cDt) + self.stringRight( + self.nLeft+nCol+47, self.nlin+nLin, + format_number(tagtext(oNode=oXML_dup, cTag='vDup'), + precision=2)) + + if nPar == 3: + nLin = 7 + nPar = 1 + nCol += 47 + nAju += 1 + nCol += nAju * (0.3) + else: + nLin += 3.3 + nPar += 1 + + # Campos adicionais XML - Condicionados a existencia de financeiro + elem_infAdic = oXML.getparent().find( + ".//{http://www.portalfiscal.inf.br/nfe}infAdic") + if elem_infAdic is not None: + codvend = elem_infAdic.find( + ".//{http://www.portalfiscal.inf.br/nfe}obsCont\ +[@xCampo='CodVendedor']") + self.string(nMr-46.5, self.nlin+7.7, + tagtext(oNode=codvend, cTag='xTexto')) + vend = elem_infAdic.find(".//{http://www.portalfiscal.inf.br/nfe}\ +obsCont[@xCampo='NomeVendedor']") + self.string(nMr-46.5, self.nlin+14.3, + tagtext(oNode=vend, cTag='xTexto')[:36]) + + self.nlin += 16 # Nr linhas ocupadas pelo bloco + + def impostos(self, oXML=None): + # Impostos + el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") + nMr = self.width-self.nRight + self.nlin += 1 + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'CÁLCULO DO IMPOSTO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 13) + self.hline(self.nLeft, self.nlin+8.5, self.width-self.nLeft) + self.vline(nMr-35, self.nlin+2, 6.5) + self.vline(nMr-72, self.nlin+2, 6.5) + self.vline(nMr-110, self.nlin+2, 6.5) + self.vline(nMr-148, self.nlin+2, 6.5) + self.vline(nMr-35, self.nlin+8.5, 6.5) + self.vline(nMr-66, self.nlin+8.5, 6.5) + self.vline(nMr-90, self.nlin+8.5, 6.5) + self.vline(nMr-119, self.nlin+8.5, 6.5) + self.vline(nMr-140, self.nlin+8.5, 6.5) + self.vline(nMr-163, self.nlin+8.5, 6.5) + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(nMr-34, self.nlin+3.8, 'VALOR TOTAL DOS PRODUTOS') + self.string(nMr-71, self.nlin+3.8, 'VALOR DO ICMS ST') + self.string(nMr-109, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS ST') + self.string(nMr-147, self.nlin+3.8, 'VALOR DO ICMS') + self.string(self.nLeft+1, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS') + self.string(nMr-34, self.nlin+10.2, 'VALOR TOTAL DA NOTA') + self.string(nMr-65, self.nlin+10.2, 'VALOR APROX TRIBUTOS') + self.string(nMr-89, self.nlin+10.2, 'VALOR DO IPI') + self.string(nMr-118, self.nlin+10.2, 'OUTRAS DESP. ACESSÓRIAS') + self.string(nMr-139, self.nlin+10.2, 'DESCONTO') + self.string(nMr-162, self.nlin+10.2, 'VALOR DO SEGURO') + self.string(self.nLeft+1, self.nlin+10.2, 'VALOR DO FRETE') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.stringRight( + self.nLeft+41, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vBC'), precision=2)) + self.stringRight( + self.nLeft+79, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vICMS'), precision=2)) + self.stringRight( + self.nLeft+117, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vBCST'), precision=2)) + self.stringRight( + nMr-36, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vST'), precision=2)) + self.stringRight( + nMr-1, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vProd'), precision=2)) + self.stringRight( + self.nLeft+26, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vFrete'), precision=2)) + self.stringRight( + self.nLeft+49, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vSeg'), precision=2)) + self.stringRight( + self.nLeft+70, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vDesc'), precision=2)) + self.stringRight( + self.nLeft+99, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vOutro'), precision=2)) + self.stringRight( + self.nLeft+123, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vIPI'), precision=2)) + self.stringRight( + self.nLeft+154, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vTotTrib'), + precision=2)) + self.stringRight( + nMr-1, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vNF'), precision=2)) + + self.nlin += 17 # Nr linhas ocupadas pelo bloco + + def transportes(self, oXML=None): + el_transp = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}transp") + nMr = self.width-self.nRight + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, + 'TRANSPORTADOR/VOLUMES TRANSPORTADOS') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 20) + self.hline(self.nLeft, self.nlin+8.6, self.width-self.nLeft) + self.hline(self.nLeft, self.nlin+15.2, self.width-self.nLeft) + self.vline(nMr-40, self.nlin+2, 13.2) + self.vline(nMr-49, self.nlin+2, 20) + self.vline(nMr-92, self.nlin+2, 6.6) + self.vline(nMr-120, self.nlin+2, 6.6) + self.vline(nMr-75, self.nlin+2, 6.6) + self.vline(nMr-26, self.nlin+15.2, 6.6) + self.vline(nMr-102, self.nlin+8.6, 6.6) + self.vline(nMr-85, self.nlin+15.2, 6.6) + self.vline(nMr-121, self.nlin+15.2, 6.6) + self.vline(nMr-160, self.nlin+15.2, 6.6) + # Labels/Fields + self.string(nMr-39, self.nlin+3.8, 'CNPJ/CPF') + self.string(nMr-74, self.nlin+3.8, 'PLACA DO VEÍCULO') + self.string(nMr-91, self.nlin+3.8, 'CÓDIGO ANTT') + self.string(nMr-119, self.nlin+3.8, 'FRETE POR CONTA') + self.string(self.nLeft+1, self.nlin+3.8, 'RAZÃO SOCIAL') + self.string(nMr-48, self.nlin+3.8, 'UF') + self.string(nMr-39, self.nlin+10.3, 'INSCRIÇÃO ESTADUAL') + self.string(nMr-48, self.nlin+10.3, 'UF') + self.string(nMr-101, self.nlin+10.3, 'MUNICÍPIO') + self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') + self.string(nMr-48, self.nlin+17, 'PESO BRUTO') + self.string(nMr-25, self.nlin+17, 'PESO LÍQUIDO') + self.string(nMr-84, self.nlin+17, 'NUMERAÇÃO') + self.string(nMr-120, self.nlin+17, 'MARCA') + self.string(nMr-159, self.nlin+17, 'ESPÉCIE') + self.string(self.nLeft+1, self.nlin+17, 'QUANTIDADE') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+7.7, + tagtext(oNode=el_transp, cTag='xNome')[:40]) + self.string(self.nLeft+71, self.nlin+7.7, + self.oFrete[tagtext(oNode=el_transp, cTag='modFrete')]) + self.string(nMr-39, self.nlin+7.7, + format_cnpj_cpf(tagtext(oNode=el_transp, cTag='CNPJ'))) + self.string(self.nLeft+1, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='xEnder')[:45]) + self.string(self.nLeft+89, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='xMun')) + self.string(nMr-48, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='UF')) + self.string(nMr-39, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='IE')) + self.string(self.nLeft+1, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='qVol')) + self.string(self.nLeft+31, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='esp')) + self.string(self.nLeft+70, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='marca')) + self.string(self.nLeft+106, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='nVol')) + self.stringRight( + nMr-27, self.nlin+21.2, + format_number(tagtext(oNode=el_transp, cTag='pesoB'), precision=3)) + self.stringRight( + nMr-1, self.nlin+21.2, + format_number(tagtext(oNode=el_transp, cTag='pesoL'), precision=3)) + + self.nlin += 23 + + def produtos(self, oXML=None, el_det=None, oPaginator=None, + list_desc=None, nHeight=29): + + nMr = self.width-self.nRight + nStep = 2.5 # Passo entre linhas + nH = 7.5 + (nHeight * nStep) # cabeçalho 7.5 + self.nlin += 1 + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'DADOS DO PRODUTO/SERVIÇO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, nH) + self.hline(self.nLeft, self.nlin+8, self.width-self.nLeft) + + self.canvas.setFont('NimbusSanL-Regu', 5.5) + # Colunas + self.vline(self.nLeft+11, self.nlin+2, nH) + self.stringcenter(self.nLeft+5.5, self.nlin+5.5, 'CÓDIGO') + self.vline(nMr-7, self.nlin+2, nH) + self.stringcenter(nMr-3.5, self.nlin+4.5, 'ALÍQ') + self.stringcenter(nMr-3.5, self.nlin+6.5, 'IPI') + self.vline(nMr-14, self.nlin+2, nH) + self.stringcenter(nMr-10.5, self.nlin+4.5, 'ALÍQ') + self.stringcenter(nMr-10.5, self.nlin+6.5, 'ICMS') + self.vline(nMr-26, self.nlin+2, nH) + self.stringcenter(nMr-20, self.nlin+5.5, 'VLR. IPI') + self.vline(nMr-38, self.nlin+2, nH) + self.stringcenter(nMr-32, self.nlin+5.5, 'VLR. ICMS') + self.vline(nMr-50, self.nlin+2, nH) + self.stringcenter(nMr-44, self.nlin+5.5, 'BC ICMS') + self.vline(nMr-64, self.nlin+2, nH) + self.stringcenter(nMr-57, self.nlin+5.5, 'VLR TOTAL') + self.vline(nMr-77, self.nlin+2, nH) + self.stringcenter(nMr-70.5, self.nlin+5.5, 'VLR UNIT') + self.vline(nMr-90, self.nlin+2, nH) + self.stringcenter(nMr-83.5, self.nlin+5.5, 'QTD') + self.vline(nMr-98, self.nlin+2, nH) + self.stringcenter(nMr-94, self.nlin+5.5, 'UNID') + self.vline(nMr-104, self.nlin+2, nH) + self.stringcenter(nMr-101, self.nlin+5.5, 'CFOP') + self.vline(nMr-110, self.nlin+2, nH) + self.stringcenter(nMr-107, self.nlin+5.5, 'CST') + self.vline(nMr-123, self.nlin+2, nH) + self.stringcenter(nMr-116.5, self.nlin+5.5, 'NCM/SH') + + nWidth_Prod = nMr-135-self.nLeft-11 + nCol_ = self.nLeft+11 + (nWidth_Prod / 2) + self.stringcenter(nCol_, self.nlin+5.5, 'DESCRIÇÃO DO PRODUTO/SERVIÇO') + + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 5) + nLin = self.nlin+10.5 + + for id in xrange(oPaginator[0], oPaginator[1]): + item = el_det[id] + el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") + el_imp = item.find( + ".//{http://www.portalfiscal.inf.br/nfe}imposto") + + el_imp_ICMS = el_imp.find( + ".//{http://www.portalfiscal.inf.br/nfe}ICMS") + el_imp_IPI = el_imp.find( + ".//{http://www.portalfiscal.inf.br/nfe}IPI") + + cCST = tagtext(oNode=el_imp_ICMS, cTag='orig') + \ + tagtext(oNode=el_imp_ICMS, cTag='CST') + vBC = tagtext(oNode=el_imp_ICMS, cTag='vBC') + vICMS = tagtext(oNode=el_imp_ICMS, cTag='vICMS') + pICMS = tagtext(oNode=el_imp_ICMS, cTag='pICMS') + + vIPI = tagtext(oNode=el_imp_IPI, cTag='vIPI') + pIPI = tagtext(oNode=el_imp_IPI, cTag='pIPI') + + self.string(self.nLeft+1, nLin, + tagtext(oNode=el_prod, cTag='cProd')) + self.stringcenter(nMr-116.5, nLin, + tagtext(oNode=el_prod, cTag='NCM')) + self.stringcenter(nMr-107, nLin, cCST) + self.stringcenter(nMr-101, nLin, + tagtext(oNode=el_prod, cTag='CFOP')) + self.stringcenter(nMr-94, nLin, + tagtext(oNode=el_prod, cTag='uCom')) + self.stringRight(nMr-77.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='qCom'), precision=4)) + self.stringRight(nMr-64.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='vUnCom'), precision=2)) + self.stringRight(nMr-50.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='vProd'), precision=2)) + self.stringRight(nMr-38.5, nLin, format_number(vBC, precision=2)) + self.stringRight(nMr-26.5, nLin, format_number(vICMS, precision=2)) + self.stringRight(nMr-7.5, nLin, format_number(pICMS, precision=2)) + + if vIPI: + self.stringRight(nMr-14.5, nLin, + format_number(vIPI, precision=2)) + if pIPI: + self.stringRight(nMr-0.5, nLin, + format_number(pIPI, precision=2)) + + self.canvas.setStrokeColor(gray) + self.hline(self.nLeft, nLin+0.5, self.width-self.nLeft) + self.canvas.setStrokeColor(black) + # Descrição Item + for des in list_desc[id]: + self.string(self.nLeft+12, nLin, des) + nLin += nStep + + self.nlin += nH + 3 + + def adicionais(self, oXML=None): + el_infAdic = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}infAdic") + + self.nlin += 2 + self.canvas.setFont('NimbusSanL-Bold', 6) + self.string(self.nLeft+1, self.nlin+1, 'DADOS ADICIONAIS') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+1, self.nlin+4, 'INFORMAÇÕES COMPLEMENTARES') + self.string((self.width/2)+1, self.nlin+4, 'RESERVADO AO FISCO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 34) + self.vline(self.width/2, self.nlin+2, 34) + # Conteúdo campos + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontSize = 6 + styleN.fontName = 'NimbusSanL-Regu' + styleN.leading = 7 + + P = Paragraph(tagtext(oNode=el_infAdic, + cTag='infCpl'), styles['Normal']) + w, h = P.wrap(92*mm, 32*mm) + P.drawOn(self.canvas, (self.nLeft+1)*mm, (self.height-self.nlin-12)*mm) + + self.nlin += 36 + + def recibo_entrega(self, oXML=None): + el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + el_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") + el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") + el_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") + + # self.nlin = self.height-self.nBottom-18 # 17 altura recibo + nW = 40 + nH = 17 + self.canvas.setLineWidth(.5) + self.rect(self.nLeft, self.nlin, + self.width-(self.nLeft+self.nRight), nH) + self.hline(self.nLeft, self.nlin+8.5, self.width-self.nRight-nW) + self.vline(self.width-self.nRight-nW, self.nlin, nH) + self.vline(self.nLeft+nW, self.nlin+8.5, 8.5) + + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+1, self.nlin+10.2, 'DATA DE RECEBIMENTO') + self.string(self.nLeft+41, self.nlin+10.2, + 'IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR') + self.stringcenter(self.width-self.nRight-(nW/2), self.nlin+2, 'NF-e') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Bold', 8) + cNF = tagtext(oNode=el_ide, cTag='nNF') + cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") + self.string(self.width-self.nRight-nW+2, self.nlin+8, "Nº %s" % (cNF)) + self.string(self.width-self.nRight-nW+2, self.nlin+14, + u"SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) + + cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) + cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'), + precision=2) + + cEnd = tagtext(oNode=el_dest, cTag='xNome') + ' - ' + cEnd += tagtext(oNode=el_dest, cTag='xLgr') + ', ' + tagtext( + oNode=el_dest, cTag='nro') + ', ' + cEnd += tagtext(oNode=el_dest, cTag='xBairro') + ', ' + tagtext( + oNode=el_dest, cTag='xMun') + ' - ' + cEnd += tagtext(oNode=el_dest, cTag='UF') + + cString = u""" + RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA + ABAIXO. EMISSÃO: %s VALOR TOTAL: %s + DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'), + cDt, cTotal, cEnd) + + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontName = 'NimbusSanL-Regu' + styleN.fontSize = 6 + styleN.leading = 7 + + P = Paragraph(cString, styleN) + w, h = P.wrap(149*mm, 7*mm) + P.drawOn(self.canvas, (self.nLeft+1)*mm, + (self.height-self.nlin-7.5)*mm) + + self.nlin += 20 + self.hline(self.nLeft, self.nlin, self.width-self.nRight) + self.nlin += 2 + + def newpage(self): + self.nlin = self.nTop + self.Page += 1 + self.canvas.showPage() + + def hline(self, x, y, width): + y = self.height - y + self.canvas.line(x*mm, y*mm, width*mm, y*mm) + + def vline(self, x, y, width): + width = self.height - y - width + y = self.height - y + self.canvas.line(x*mm, y*mm, x*mm, width*mm) + + def rect(self, col, lin, nWidth, nHeight, fill=False): + lin = self.height - nHeight - lin + self.canvas.rect(col*mm, lin*mm, nWidth*mm, nHeight*mm, + stroke=True, fill=fill) + + def string(self, x, y, value): + y = self.height - y + self.canvas.drawString(x*mm, y*mm, value) + + def stringRight(self, x, y, value): + y = self.height - y + self.canvas.drawRightString(x*mm, y*mm, value) + + def stringcenter(self, x, y, value): + y = self.height - y + self.canvas.drawCentredString(x*mm, y*mm, value) + + def get_buffer(self): + pdf_out = self.oPDF_IO.getvalue() + self.oPDF_IO.close() + return pdf_out + + +# Exemplo de uso + +if __name__ == "__main__": + xml_string = open( + "/home/danimar/Downloads/nfe-envio-2017-02-13-10-10.xml", "r").read() + xml_element = etree.fromstring(xml_string) + + oDanfe = danfe(list_xml=[xml_element]) + oFile = open('/home/danimar/DANFE.pdf', 'w') + oFile.write(oDanfe.get_buffer()) + oFile.close() From 374e0ec283af6311d31c3ab5777f1157ee6f6e1d Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Sat, 25 Feb 2017 12:54:34 -0300 Subject: [PATCH 2/2] Teste para gerar a danfe - Ajuste na logo e dados da empresa --- pytrustnfe/nfe/danfe.py | 63 ++++++++++++++++++------------------ pytrustnfe/test/XMLs/NFe00000857.xml | 46 ++++++++++++++++++++++++++ pytrustnfe/test/test_danfe.py | 22 +++++++++++++ requirements.txt | 1 + setup.py | 1 + 5 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 pytrustnfe/test/XMLs/NFe00000857.xml create mode 100644 pytrustnfe/test/test_danfe.py diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 295cc64..bbaddf0 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -3,12 +3,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # Classe para geração de PDF da DANFE a partir de xml etree.fromstring -# TO DO -# Layout Paisagem -# Logo imagem da empresa from cStringIO import StringIO as IO -from lxml import etree from textwrap import wrap from reportlab.lib import utils @@ -18,6 +14,7 @@ from reportlab.lib.pagesizes import A4 from reportlab.lib.colors import black, gray from reportlab.graphics.barcode import code128 from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.enums import TA_CENTER from reportlab.platypus import Paragraph, Image @@ -58,6 +55,12 @@ def tagtext(oNode=None, cTag=None): cText = '' return cText +REGIME_TRIBUTACAO = { + '1': 'Simples Nacional', + '2': 'Simples Nacional, excesso sublimite de receita bruta', + '3': 'Regime Normal' +} + def get_image(path, width=1*cm): img = utils.ImageReader(path) @@ -68,7 +71,7 @@ def get_image(path, width=1*cm): class danfe(object): def __init__(self, sizepage=A4, list_xml=None, recibo=True, - orientation='portrait'): + orientation='portrait', logo=None): self.width = 210 # 21 x 29,7cm self.height = 297 self.nLeft = 10 @@ -76,6 +79,7 @@ class danfe(object): self.nTop = 7 self.nBottom = 15 self.nlin = self.nTop + self.logo = logo self.oFrete = {'0': '0 - Emitente', '1': '1 - Dest/Remet', '2': '2 - Terceiros', @@ -248,31 +252,39 @@ class danfe(object): styles = getSampleStyleSheet() styleN = styles['Normal'] - styleN.fontSize = 11 - styleN.fontName = 'NimbusSanL-Regu' + styleN.fontSize = 10 + styleN.fontName = 'NimbusSanL-Bold' + styleN.alignment = TA_CENTER # Razão Social emitente P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) w, h = P.wrap(55*mm, 50*mm) P.drawOn(self.canvas, (self.nLeft+30)*mm, - (self.height-self.nlin-10)*mm) + (self.height-self.nlin-12)*mm) - logo = '/home/danimar/Downloads/innova.png' - img = get_image(logo, width=2*cm) - img.drawOn(self.canvas, (self.nLeft+5)*mm, (self.height-self.nlin-22)*mm) + if self.logo: + img = get_image(self.logo, width=2*cm) + img.drawOn(self.canvas, (self.nLeft+5)*mm, + (self.height-self.nlin-22)*mm) cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( oNode=elem_emit, cTag='nro') + ' - ' - cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + ' - ' + tagtext( + cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '
' + tagtext( oNode=elem_emit, cTag='xMun') + ' - ' + cEnd += 'Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + '
' cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext( oNode=elem_emit, cTag='CEP') - cEnd += ' - Fone: ' + tagtext(oNode=elem_emit, cTag='fone') - styleN.fontSize = 8 + + regime = tagtext(oNode=elem_emit, cTag='CRT') + cEnd += u'
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime]) + + styleN.fontName = 'NimbusSanL-Regu' + styleN.fontSize = 7 styleN.leading = 10 P = Paragraph(cEnd, styleN) - w, h = P.wrap(80*mm, 30*mm) - P.drawOn(self.canvas, (self.nLeft+5)*mm, (self.height-self.nlin-30)*mm) + w, h = P.wrap(55*mm, 30*mm) + P.drawOn(self.canvas, (self.nLeft+30)*mm, + (self.height-self.nlin-31)*mm) # Homologação if tagtext(oNode=elem_ide, cTag='tpAmb') == '2': @@ -695,7 +707,7 @@ obsCont[@xCampo='NomeVendedor']") P = Paragraph(tagtext(oNode=el_infAdic, cTag='infCpl'), styles['Normal']) w, h = P.wrap(92*mm, 32*mm) - P.drawOn(self.canvas, (self.nLeft+1)*mm, (self.height-self.nlin-12)*mm) + P.drawOn(self.canvas, (self.nLeft+1)*mm, (self.height-self.nlin-17)*mm) self.nlin += 36 @@ -792,20 +804,7 @@ obsCont[@xCampo='NomeVendedor']") y = self.height - y self.canvas.drawCentredString(x*mm, y*mm, value) - def get_buffer(self): + def writeto_pdf(self, fileObj): pdf_out = self.oPDF_IO.getvalue() self.oPDF_IO.close() - return pdf_out - - -# Exemplo de uso - -if __name__ == "__main__": - xml_string = open( - "/home/danimar/Downloads/nfe-envio-2017-02-13-10-10.xml", "r").read() - xml_element = etree.fromstring(xml_string) - - oDanfe = danfe(list_xml=[xml_element]) - oFile = open('/home/danimar/DANFE.pdf', 'w') - oFile.write(oDanfe.get_buffer()) - oFile.close() + fileObj.write(pdf_out) diff --git a/pytrustnfe/test/XMLs/NFe00000857.xml b/pytrustnfe/test/XMLs/NFe00000857.xml new file mode 100644 index 0000000..cb47a9c --- /dev/null +++ b/pytrustnfe/test/XMLs/NFe00000857.xml @@ -0,0 +1,46 @@ +3513416577Venda Não Contribuintes15518572017-02-24T11:39:30-00:002017-02-24T11:39:30-00:0012355030811721100Odoo Brasil 1021332917000163TRUSTCODE TECNOLOGIA DA INFORMAÇÃO LTDATrustcodeRua Vinicius de Moraes42Córrego Grande3550308FlorianópolisSC880372401058Brasil1135302850144013873114111370685000184NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCALAV AMAZONAS1193Centro4314902Porto AlegreRS902405421058Brasil9VTS-L1231100907VENTIS PRETO- O2 CO H2S DIFUSAO902710006108UN1.0100.00100.00UN1.03562.2695.0010.000102999493657.260.000.0007070.000.000.000.000.003562.2695.000.000.000.000.000.000.000.003657.260.001SEDEXFalse - False, FalseINV/2017/01263562.263657.26012017-03-161828.63022017-04-151828.63PERMITE O APROVEITAMENTO DO CRÉDITO DE ICMS NO VALOR CORRESPONDENTE À ALÍQUOTA DE 1,25%, NOS TERMOS DO ART. 23 DA LC 123/2006 >>IMPORTANTE<< P/ LIQUIDACAO DESTA NF, EFETUE DEPOSITO IDENTIFICADO NO BANCO BRADESCO AG: 1992-5, C/c: 4897-6 PEDIDO DE COMPRAS: OC 0045-05/2017 N/S 16122WZ-021 (Orçamento SO176) **VENCIMENTO: 15/04/2017 ==> .OC 0045-05/2017 N/S 16122WZ-021bNLOBxpMk5J6rrz37coB8/pvTBE=Y++vItLsZAbwzM/YDsgGqSn2+u035OSigoskd1x7DDJuAFuM0imbOuC20TAJPODcZCFxfqO1VTFCVgMmJUtHGzwvVdr3DSlbxIevfTF0nNwBYN2LzQVY2R/495ro2Vw2waKfOU+O2IZrKlFxBfu91Vv/JRpbECElwZaDK1BEp2ekGkB0tHfisGbQu1WFR8HBqwcyn8khhScO8nE7S+MR8uyEqf5057AiZZr1/vG/vyNhN1yzP8FFT3kHOG3w2aNe0H85s9spUrSC5hOAIy0yD6/NUUfH9AOOlER+cCLgLT52W7I5nnxC7dgEzG6YQffy1XGd/TQ4RC7ppKwmkVFaoQ==MIIIPzCCBiegAwIBAgIQYdesnYUNG8VPne0qhTeKOzANBgkqhkiG9w0BAQsFADB4 +MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE2MDQGA1UECxMtU2Vj +cmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgZG8gQnJhc2lsIC0gUkZCMRwwGgYD +VQQDExNBQyBDZXJ0aXNpZ24gUkZCIEc0MB4XDTE2MDUxMDAwMDAwMFoXDTE3MDUw +OTIzNTk1OVowgekxCzAJBgNVBAYTAkJSMRMwEQYDVQQKFApJQ1AtQnJhc2lsMQsw +CQYDVQQIEwJTUDESMBAGA1UEBxQJU2FvIFBhdWxvMTYwNAYDVQQLFC1TZWNyZXRh +cmlhIGRhIFJlY2VpdGEgRmVkZXJhbCBkbyBCcmFzaWwgLSBSRkIxFjAUBgNVBAsU +DVJGQiBlLUNOUEogQTExJDAiBgNVBAsUG0F1dGVudGljYWRvIHBvciBBUiBTdW5z +aGluZTEuMCwGA1UEAxMlTEVaIEFNQklFTlRBTCBMVERBIEVQUDoyMTMzMjkxNzAw +MDE2MzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEG6j0uXIvvHlMz +0IGzuY/vuFQncIoSE+cBUk0uq6J3dtmGAg4oaVWCHUfHbX9s2Ag1jIG+PFAo2dlt +sbLSEji74XhD+IpM/9aHm3ke8kb05ay+bYRuUjTNSwUbslT1+amAmIu7m1yPBi6u +v3+/Lj2I0g7VeBBAjv/TiBG0VRCURXvKrwWrv2Lpybo/yDnENGtRqQHihqeYFKin +nDzBsMbv4ripbi3XiAgcy/bF6NFgVMqxrNnGvSiSUhDRkmceVFIysRXUMke02Qo1 +Q5Ik1j1goUIHP44QOruXCMiT0yOK8u0qNAXR0yzSaWcBR2aJCeWgFg7sNbB50Qcx +c+2GKUECAwEAAaOCA1EwggNNMIG2BgNVHREEga4wgaugPQYFYEwBAwSgNAQyMTYw +NjE5ODYzNDEzNzgyODg2NTAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMzczU1NQU1Cg +IgYFYEwBAwKgGQQXTEVPTkFSRE8gREUgTElNQSBTQU5UT1OgGQYFYEwBAwOgEAQO +MjEzMzI5MTcwMDAxNjOgFwYFYEwBAwegDgQMMDAwMDAwMDAwMDAwgRJ3YWduZXJA +emVsbC5jb20uYnIwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBQukerWbeWyWYLcOIUp +djQWVjzQPjAOBgNVHQ8BAf8EBAMCBeAwfwYDVR0gBHgwdjB0BgZgTAECAQwwajBo +BggrBgEFBQcCARZcaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9y +ZXBvc2l0b3Jpby9kcGMvQUNfQ2VydGlzaWduX1JGQi9EUENfQUNfQ2VydGlzaWdu +X1JGQi5wZGYwggEWBgNVHR8EggENMIIBCTBXoFWgU4ZRaHR0cDovL2ljcC1icmFz +aWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jpby9sY3IvQUNDZXJ0aXNpZ25S +RkJHNC9MYXRlc3RDUkwuY3JsMFagVKBShlBodHRwOi8vaWNwLWJyYXNpbC5vdXRy +YWxjci5jb20uYnIvcmVwb3NpdG9yaW8vbGNyL0FDQ2VydGlzaWduUkZCRzQvTGF0 +ZXN0Q1JMLmNybDBWoFSgUoZQaHR0cDovL3JlcG9zaXRvcmlvLmljcGJyYXNpbC5n +b3YuYnIvbGNyL0NlcnRpc2lnbi9BQ0NlcnRpc2lnblJGQkc0L0xhdGVzdENSTC5j +cmwwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMIGbBggrBgEFBQcBAQSB +jjCBizBfBggrBgEFBQcwAoZTaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNv +bS5ici9yZXBvc2l0b3Jpby9jZXJ0aWZpY2Fkb3MvQUNfQ2VydGlzaWduX1JGQl9H +NC5wN2MwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNlcnRpc2lnbi5jb20uYnIw +DQYJKoZIhvcNAQELBQADggIBAFIUBrNIyC4kBap/7hCW63tQhA/WNnWDNYpKM5wN +zwApVV2bqFMJURzO/7AUrHu7uZS1p/Ubo+w2dFjmnmj5DniQkY85Sd6HNa1fukJY +PK13wcUMVHMjeevIAcxnYraNdN4BIz1Svl6A8leGFgIEuDUll7Td+R7+aA8N5JYQ +dFFIe2VxvJNbWP/WA49oI8U2wkoPTfOZtfrgKf2msHm3FnTfnmyOPhIf8L31iFt6 +MbKuFjOGIaWu+Z/gRDqj/EbFcEMUrDbeIYqz2724ZGBOJrkjHO7DBqXXoN9pzCTO +RB5+gILMEnMS7zFsCuLOtLVohxgYUzg8p4Fy3nsHEzb/7IDVOnKLfjh/c5GSTvOa +JT6qznYV2yav7NyuUSNUv+5bCIBNk45+qrQ8DwpsLBsFa+RLfCwvYVK95ad/xVgJ +QosPJuzW3t0fU/FWbc00sZWV6lgBPyWhdF8EodaRIWC+EOC2wJbODyw+vdX8pUxT +TUJKV2iAP8206gR2h07o2CZgXckJGJQ5MnBUbS78AaITXZ5JlPaS7ZdU9zWY3kD+ +j5YERs0+UweijKi5eHZioGRZRDZ2uksh1wrgeLFLWuiSNaPFYVVrQ/ZGo+E5uVNl +8FuoR6P9TZjx1/A4XjqLQ9yPoPWgIWe14Vh/76dVcgz3ElWMbmPCDoc/wX+FoHX1 +Fbux +2SP_NFE_PL_008i2351702213329170001635500100000085711341657772017-02-24T08:39:31-03:00135170000807903bNLOBxpMk5J6rrz37coB8/pvTBE=100Autorizado o uso da NF-e diff --git a/pytrustnfe/test/test_danfe.py b/pytrustnfe/test/test_danfe.py new file mode 100644 index 0000000..c73bfd9 --- /dev/null +++ b/pytrustnfe/test/test_danfe.py @@ -0,0 +1,22 @@ +# coding=utf-8 + +import tempfile +import os.path +import unittest +from lxml import etree +from pytrustnfe.nfe.danfe import danfe + + +class test_danfe(unittest.TestCase): + + caminho = os.path.dirname(__file__) + + def test_can_generate_danfe(self): + path = os.path.join(os.path.dirname(__file__), 'XMLs') + xml_string = open(os.path.join(path, 'NFe00000857.xml'), "r").read() + xml_element = etree.fromstring(xml_string) + + oDanfe = danfe(list_xml=[xml_element]) + + with tempfile.TemporaryFile(mode='w') as oFile: + oDanfe.writeto_pdf(oFile) diff --git a/requirements.txt b/requirements.txt index a8fbe02..e343fde 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ eight >= 0.3.0, < 0.4 cryptography >= 1.4, < 1.8 pyOpenSSL >= 16.0.0, < 17 certifi >= 2015.11.20.1 +reportlab diff --git a/setup.py b/setup.py index 223c55c..9d396e9 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,7 @@ later (LGPLv2+)', 'lxml >= 3.6.0, < 3.7', 'suds >= 0.4', 'suds_requests >= 0.3', + 'reportlab' ], test_suite='nose.collector', tests_require=[