You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
36 KiB
830 lines
36 KiB
# -*- 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
|
|
|
|
|
|
from cStringIO import StringIO as IO
|
|
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.lib.enums import TA_CENTER
|
|
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
|
|
|
|
|
|
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)
|
|
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', logo=None):
|
|
self.width = 210 # 21 x 29,7cm
|
|
self.height = 297
|
|
self.nLeft = 10
|
|
self.nRight = 10
|
|
self.nTop = 7
|
|
self.nBottom = 8
|
|
self.nlin = self.nTop
|
|
self.logo = logo
|
|
self.oFrete = {'0': '0 - Emitente',
|
|
'1': '1 - Destinatário',
|
|
'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 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
|
|
|
|
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, list_cod_prod=list_cod_prod)
|
|
|
|
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,
|
|
list_cod_prod=list_cod_prod)
|
|
|
|
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.hline(self.nLeft+85, self.nlin+1, 125)
|
|
|
|
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+5, '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 = 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-12)*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') + '<br />' + tagtext(
|
|
oNode=elem_emit, cTag='xMun') + ' - '
|
|
cEnd += 'Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + '<br />'
|
|
cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext(
|
|
oNode=elem_emit, cTag='CEP')
|
|
|
|
regime = tagtext(oNode=elem_emit, cTag='CRT')
|
|
cEnd += u'<br />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(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':
|
|
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'))
|
|
cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CNPJ'))
|
|
if cnpj_cpf == '..-' or not cnpj_cpf:
|
|
cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CPF'))
|
|
self.string(nMr-69, self.nlin+7.5, cnpj_cpf)
|
|
cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi'))
|
|
self.string(nMr-24, self.nlin+7.7, cDt + ' ' + cHr)
|
|
cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt'))
|
|
self.string(nMr-24, self.nlin+14.3, cDt + ' ' + cHr) # 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-65, self.nlin+2, 6.5)
|
|
self.vline(nMr-95, self.nlin+2, 6.5)
|
|
self.vline(nMr-125, self.nlin+2, 6.5)
|
|
self.vline(nMr-155, self.nlin+2, 6.5)
|
|
self.vline(nMr-35, self.nlin+8.5, 6.5)
|
|
self.vline(nMr-65, self.nlin+8.5, 6.5)
|
|
self.vline(nMr-95, self.nlin+8.5, 6.5)
|
|
self.vline(nMr-125, self.nlin+8.5, 6.5)
|
|
self.vline(nMr-155, self.nlin+8.5, 6.5)
|
|
# Labels
|
|
self.canvas.setFont('NimbusSanL-Regu', 5)
|
|
self.string(self.nLeft+1, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS')
|
|
self.string(nMr-154, self.nlin+3.8, 'VALOR DO ICMS')
|
|
self.string(nMr-124, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS ST')
|
|
self.string(nMr-94, self.nlin+3.8, 'VALOR DO ICMS ST')
|
|
self.string(nMr-64, self.nlin+3.8, 'VALOR APROX TRIBUTOS')
|
|
self.string(nMr-34, self.nlin+3.8, 'VALOR TOTAL DOS PRODUTOS')
|
|
|
|
self.string(self.nLeft+1, self.nlin+10.2, 'VALOR DO FRETE')
|
|
self.string(nMr-154, self.nlin+10.2, 'VALOR DO SEGURO')
|
|
self.string(nMr-124, self.nlin+10.2, 'DESCONTO')
|
|
self.string(nMr-94, self.nlin+10.2, 'OUTRAS DESP. ACESSÓRIAS')
|
|
self.string(nMr-64, self.nlin+10.2, 'VALOR DO IPI')
|
|
self.string(nMr-34, self.nlin+10.2, 'VALOR TOTAL DA NOTA')
|
|
|
|
# Conteúdo campos
|
|
self.canvas.setFont('NimbusSanL-Regu', 8)
|
|
self.stringRight(
|
|
self.nLeft+34, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vBC'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+64, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vICMS'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+94, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vBCST'), precision=2))
|
|
self.stringRight(
|
|
nMr-66, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vST'), precision=2))
|
|
self.stringRight(
|
|
nMr-36, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vTotTrib'),
|
|
precision=2))
|
|
self.stringRight(
|
|
nMr-1, self.nlin+7.7,
|
|
format_number(tagtext(oNode=el_total, cTag='vProd'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+34, self.nlin+14.1,
|
|
format_number(tagtext(oNode=el_total, cTag='vFrete'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+64, self.nlin+14.1,
|
|
format_number(tagtext(oNode=el_total, cTag='vSeg'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+94, self.nlin+14.1,
|
|
format_number(tagtext(oNode=el_total, cTag='vDesc'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+124, self.nlin+14.1,
|
|
format_number(tagtext(oNode=el_total, cTag='vOutro'), precision=2))
|
|
self.stringRight(
|
|
self.nLeft+154, self.nlin+14.1,
|
|
format_number(tagtext(oNode=el_total, cTag='vIPI'), 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, list_cod_prod=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+15, self.nlin+2, nH)
|
|
self.stringcenter(self.nLeft+7.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-96, self.nlin+2, nH)
|
|
self.stringcenter(nMr-93, self.nlin+5.5, 'UNID')
|
|
self.vline(nMr-102, self.nlin+2, nH)
|
|
self.stringcenter(nMr-99, self.nlin+5.5, 'CFOP')
|
|
self.vline(nMr-108, self.nlin+2, nH)
|
|
self.stringcenter(nMr-105, self.nlin+5.5, 'CST')
|
|
self.vline(nMr-117, self.nlin+2, nH)
|
|
self.stringcenter(nMr-112.5, self.nlin+5.5, 'NCM/SH')
|
|
|
|
nWidth_Prod = nMr-135-self.nLeft-11
|
|
nCol_ = self.nLeft+20 + (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='CSOSN')
|
|
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.stringcenter(nMr-112.5, nLin,
|
|
tagtext(oNode=el_prod, cTag='NCM'))
|
|
self.stringcenter(nMr-105, nLin, cCST)
|
|
self.stringcenter(nMr-99, nLin,
|
|
tagtext(oNode=el_prod, cTag='CFOP'))
|
|
self.stringcenter(nMr-93, 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))
|
|
|
|
# Código Item
|
|
line_cod = nLin
|
|
for des in list_cod_prod[id]:
|
|
self.string(self.nLeft+0.2, line_cod, des)
|
|
line_cod += nStep
|
|
|
|
# Descrição Item
|
|
line_desc = nLin
|
|
for des in list_desc[id]:
|
|
self.string(self.nLeft+15.5, line_desc, des)
|
|
line_desc += nStep
|
|
|
|
nLin = max(line_cod, line_desc)
|
|
self.canvas.setStrokeColor(gray)
|
|
self.hline(self.nLeft, nLin-2, self.width-self.nLeft)
|
|
self.canvas.setStrokeColor(black)
|
|
|
|
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, 42)
|
|
self.vline(self.width/2, self.nlin+2, 42)
|
|
# Conteúdo campos
|
|
styles = getSampleStyleSheet()
|
|
styleN = styles['Normal']
|
|
styleN.fontSize = 6
|
|
styleN.fontName = 'NimbusSanL-Regu'
|
|
styleN.leading = 7
|
|
fisco = tagtext(oNode=el_infAdic, cTag='infAdFisco')
|
|
observacoes = tagtext(oNode=el_infAdic, cTag='infCpl')
|
|
if fisco:
|
|
observacoes = fisco + ' ' + observacoes
|
|
P = Paragraph(observacoes, styles['Normal'])
|
|
w, h = P.wrap(92*mm, 32*mm)
|
|
altura = (self.height-self.nlin-5)*mm
|
|
P.drawOn(self.canvas, (self.nLeft+1)*mm, altura - h)
|
|
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)*mm) - h)
|
|
|
|
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 writeto_pdf(self, fileObj):
|
|
pdf_out = self.oPDF_IO.getvalue()
|
|
self.oPDF_IO.close()
|
|
fileObj.write(pdf_out)
|