# -*- coding: utf-8 -*-
import datetime
import re
from decimal import Decimal
[docs]class BoletoException(Exception):
def __init__(self, message ):
Exception.__init__(self, message)
[docs]def custom_property(name, num_length):
'''
Function to create properties on boleto
It accepts a number with or without a DV and zerofills it
'''
internal_attr = '_%s'%name
def _set_attr(self,val):
val = val.split('-')
if len(val) is 1:
val[0] = str(val[0]).zfill(num_length)
setattr(self, internal_attr, ''.join(val))
elif len(val) is 2:
val[0] = str(val[0]).zfill(num_length)
setattr(self, internal_attr, '-'.join(val))
else:
raise BoletoException('Wrong value format')
return property(
lambda self: getattr(self, internal_attr),
_set_attr,
lambda self: delattr(self, internal_attr),
name
)
[docs]class BoletoData(object):
def __init__(self, *args, **kwargs):
self.aceite = "N"
self.agencia_cedente = ""
self.carteira = ""
self.cedente = ""
self.codigo_banco = ""
self.conta_cedente = ""
self.data_documento = ""
self.data_processamento = datetime.date.today()
self.data_vencimento = ""
self.demonstrativo = []
self.especie = "R$"
self.especie_documento = ""
self.instrucoes = []
self.local_pagamento = "Pagável em qualquer banco até o vencimento"
self.logo_image_path = ""
self.moeda = "9"
self.numero_documento = ""
self.quantidade = ""
self.sacado_nome = ""
self.sacado_cidade = ""
self.sacado_uf = ""
self.sacado_endereco = ""
self.sacado_bairro = ""
self.sacado_cep = ""
@property
[docs] def barcode(self):
'''
Returns string used to generate barcodes
It should be implemented by derived class
'''
raise NotImplementedError(
'This method has not been implemented by this class'
)
@property
[docs] def dv_nosso_numero(self):
'''
Returns nosso número DV
It should be implemented by derived class
'''
raise NotImplementedError(
'This method has not been implemented by this class'
)
[docs] def calculate_dv_barcode(self, line):
'''
Calculates de DV for barcode
It should be implemented by derived class
'''
resto2 = self.modulo11(line,9,1)
if resto2 in [0, 1, 10]:
dv = 1
else:
dv = 11 - resto2
return dv
nosso_numero = custom_property('nosso_numero', 13)
agencia_cedente = custom_property('agencia_cedente', 4)
conta_cedente = custom_property('conta_cedente', 7)
def _get_valor(self):
try:
return "%.2f" % self._valor
except AttributeError:
pass
def _set_valor(self, val):
if type(val) is Decimal:
self._valor = val
else:
self._valor = Decimal(str(val), 2)
valor = property(_get_valor, _set_valor)
def _get_valor_documento(self):
try:
return "%.2f" % self._valor_documento
except AttributeError:
pass
def _set_valor_documento(self, val):
if type(val) is Decimal:
self._valor_documento = val
else:
self._valor_documento = Decimal(str(val), 2)
valor_documento = property(
_get_valor_documento,
_set_valor_documento
)
def _instrucoes_get(self):
try:
return self._instrucoes
except AttributeError:
pass
def _instrucoes_set(self, list_inst):
if len(list_inst) > 7:
raise BoletoException(
u'Número de linhas de instruções maior que 7')
for line in list_inst:
if len(line) > 90:
raise BoletoException(
u'Linha de instruções possui mais que 90 caracteres')
self._instrucoes = list_inst
instrucoes = property(_instrucoes_get, _instrucoes_set)
def _demonstrativo_get(self):
try:
return self._demonstrativo
except AttributeError:
pass
def _demonstrativo_set(self,list_dem):
if len(list_dem) > 12:
raise BoletoException(
u'Número de linhas de demonstrativo maior que 12')
for line in list_dem:
if len(line) > 90:
raise BoletoException(
u'Linha de demonstrativo possui mais que 90 caracteres')
self._demonstrativo = list_dem
demonstrativo = property(_demonstrativo_get, _demonstrativo_set)
def _sacado_get(self):
if not hasattr(self, '_sacado'):
self.sacado = [
self.sacado_nome,
self.sacado_endereco,
'%s - %s - %s - %s' % (
self.sacado_bairro,
self.sacado_cidade,
self.sacado_uf,
self.sacado_cep
)
]
return self._sacado
def _sacado_set(self, list_sacado):
if len(list_sacado) > 3:
raise BoletoException(u'Número de linhas do sacado maior que 3')
for line in list_sacado:
if len(line) > 80:
raise BoletoException(
u'Linha de sacado possui mais que 80 caracteres')
self._sacado = list_sacado
sacado = property(_sacado_get, _sacado_set)
@property
[docs] def fator_vencimento(self):
date_ref = datetime.date(2000,7,3) # Fator = 1000
delta = self.data_vencimento - date_ref
fator = delta.days + 1000
return fator
@property
[docs] def agencia_conta_cedente(self):
return "%s/%s" % (self.agencia_cedente, self.conta_cedente)
@property
[docs] def codigo_dv_banco(self):
cod = "%s-%s" % (self.codigo_banco, self.modulo11(self.codigo_banco))
return cod
@property
[docs] def linha_digitavel(self):
'''
Monta a linha que o cliente pode utilizar para digitar se o código
de barras não puder ser lido
Posição Conteúdo
1 a 3 Número do banco
4 Código da Moeda - 9 para Real
5 Digito verificador do Código de Barras
6 a 19 Valor (12 inteiros e 2 decimais)
20 a 44 Campo Livre definido por cada banco
'''
linha = self.barcode
if not linha:
BoletoException("Boleto doesn't have a barcode")
p1 = linha[0:4]
p2 = linha[19:24]
p3 = self.modulo10("%s%s"%(p1,p2))
p4 = "%s%s%s" %(p1,p2,p3)
p5 = p4[0:5]
p6 = p4[5:]
campo1 = "%s.%s" %(p5,p6)
p1 = linha[24:34]
p2 = self.modulo10(p1)
p3 = "%s%s" %(p1,p2)
p4 = p3[0:5]
p5 = p3[5:]
campo2 = "%s.%s" %(p4,p5)
p1 = linha[34:44]
p2 = self.modulo10(p1)
p3 = "%s%s" %(p1,p2)
p4 = p3[0:5]
p5 = p3[5:]
campo3 = "%s.%s" %(p4,p5)
campo4 = linha[4]
campo5 = linha[5:19]
return "%s %s %s %s %s" %(campo1,campo2,campo3,campo4,campo5)
@staticmethod
@staticmethod
[docs] def formata_texto(texto, tamanho):
if len(texto) > tamanho:
raise BoletoException(
u'Tamanho em caracteres do texto está maior que o permitido' )
return texto.ljust(tamanho)
@staticmethod
[docs] def modulo10(num):
soma = 0
peso = 2
for i in range(len(num)-1,-1,-1):
parcial = int(num[i]) * peso
if parcial > 9:
s = "%d" % parcial
parcial = int(s[0])+int(s[1])
soma += parcial
if peso == 2:
peso = 1
else:
peso = 2
resto10 = soma % 10
if resto10 == 0:
modulo10 = 0
else:
modulo10 = 10 - resto10
return modulo10
@staticmethod
[docs] def modulo11(num,base=9,r=0):
soma=0
fator=2
for i in range(len(str(num))).__reversed__():
parcial10 = int(num[i]) * fator
soma += parcial10
if fator == base:
fator = 1
fator += 1
if r == 0:
soma = soma * 10
digito = soma % 11
if digito == 10:
digito = 0
return digito
if r == 1:
resto = soma % 11
return resto