PDF generation advanced configuration¶
In order to redefine the PDF layout you must create a new function in the settings.py file. For this example we will call it pdf_invoice_new. Once the new function is created you can modify it to change the default PDF layout.
In order to edit the settings.py file please see How to edit settings.py file.
def footer_pagination(canvas, doc):
canvas.saveState()
canvas.translate(0, 0)
canvas.setFont('Helvetica-Bold', 6)
width, height = pagesizes.A4
canvas.drawCentredString(width / 2, cm, "Page {}".format(doc.page))
canvas.restoreState()
def pdf_invoice_new(
pdf_file, invoice_display_number, invoice_status, customer_details, company_details,
invoice_items, invoice_totals, invoice_issue_date=None, invoice_due_date=None,
text_after_invoice_items=None, invoice_title=None, invoice_author=None, invoice_creator=None,
invoice_lang=None, invoice_subject=None, invoice_currency='',
):
# Define paragraph styles
title_style = ParagraphStyle(name='invTitleStyle',
fontName='Helvetica-Bold',
fontSize=16,
textColor='#404040',
testTransform='upper',
leading=20)
sub_title_style = ParagraphStyle(name='invTitleStyle',
fontName='Helvetica-Bold',
fontSize=8,
leading=10,
textColor='#404040',
testTransform='upper')
norm_style = ParagraphStyle(name='invNormStyle',
fontName='Helvetica',
textColor='#606060',
fontSize=8,
leading=10)
norm_style_bold = ParagraphStyle(name='invNormStyle',
fontName='Helvetica-Bold',
textColor='#606060',
fontSize=8,
leading=10)
totals_style_bold = ParagraphStyle(name='invNormStyle',
fontName='Helvetica-Bold',
textColor='#000000',
fontSize=8,
leading=10,
alignment=enums.TA_RIGHT)
cost_style = ParagraphStyle(name='invCostStyle',
fontName='Helvetica',
textColor='#000000',
fontSize=8,
leading=10,
alignment=enums.TA_CENTER)
norm_style_right = ParagraphStyle(name='invNormStyleRight',
fontName='Helvetica',
textColor='#606060',
fontSize=8,
leading=10,
alignment=enums.TA_RIGHT)
head_style = ParagraphStyle(name='headStyle',
fontName='Helvetica',
textColor='#ffffff',
backColor='#404040',
fontSize=10,
leading=12)
# Begin invoice pdf generation
invoice_title = invoice_title or invoice_display_number
invoice_author = invoice_author or 'Fleio Billing'
invoice_creator = invoice_creator or 'Fleio Billing'
invoice_lang = invoice_lang or 'en'
invoice_subject = invoice_subject or '{} {}'.format(invoice_display_number, invoice_status)
doc = SimpleDocTemplate(pdf_file,
pagesize=pagesizes.A4,
leftMargin=cm,
rightMargin=cm,
topMargin=cm,
bottomMargin=cm,
title=invoice_title,
author=invoice_author,
creator=invoice_creator,
lang=invoice_lang,
subject=invoice_subject)
# noinspection PyListCreation
pdf_story = [Spacer(1, cm)]
# Add invoice display number and header
pdf_story.append(Paragraph(invoice_display_number, style=title_style))
pdf_story.append(Paragraph(invoice_status, style=sub_title_style))
pdf_story.append(Spacer(1, cm / 3))
# Add customer and company info
customer_paragraph = []
# adds issue and due dates
if invoice_issue_date:
customer_paragraph.append(Paragraph(_('Issue Date: {}').format(invoice_issue_date), norm_style))
if invoice_due_date:
customer_paragraph.append(Paragraph(_('Due Date: {}').format(invoice_due_date), norm_style))
# adds customer info
customer_paragraph.append(Paragraph(_('Invoiced to:'), norm_style_bold))
customer_details = customer_details or 'Customer details missing'
for cinf in customer_details.splitlines():
customer_paragraph.append(Paragraph(cinf, norm_style))
# adds company info
company_details = company_details or 'Company details missing'
company_paragraph = [Paragraph(cinf, norm_style_right) for cinf in company_details.splitlines()]
invoice_info = Table(data=[[customer_paragraph, company_paragraph]],
style=[('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LEFTPADDING', (0, 0), (0, 0), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0)])
pdf_story.append(invoice_info)
# Add invoice items
# Set some items styles first
table_style = [('BACKGROUND', (0, 0), (-1, 0), '#404040'),
('ALIGN', (3, 0), (-1, -1), 'CENTER'),
('VALIGN', (3, 0), (-1, -1), 'MIDDLE'),
('TEXTCOLOR', (1, 0), (-1, 0), '#ffffff'),
('SPAN', (0, 0), (2, 0)),
('LINEAFTER', (0, 1), (-2, -len(invoice_totals) - 1), 0.1, '#707070', None, (2, 2, 2)),
('LINEBELOW', (0, 1), (-1, -len(invoice_totals) - 1), 0.1, '#707070', None, (2, 2, 2))]
items_table = []
items_header = [Paragraph(_('Description'), head_style), '', '', _('Quantity'), _('Unit Price'), _('Cost')]
items_table.append(items_header)
last_item_num = 1
for item in invoice_items:
item_description = item['description']
for option in item.get('options', []):
item_description = '{} <br/> - {}'.format(item_description, option['display'])
if option.get('price', 0) > 0:
item_description = '{} ({} {})'.format(item_description, option['price'], invoice_currency)
items_table.append([Paragraph(item_description, norm_style), '', '',
Paragraph(str(item.get('quantity', '1')), cost_style),
Paragraph(str(item.get('unit_price')), cost_style),
Paragraph(str(item.get('cost')), cost_style)])
table_style.append(('SPAN', (0, last_item_num), (2, last_item_num)))
last_item_num += 1
# Add totals
for inv_total in invoice_totals:
items_table.append(['', '', '', '',
Paragraph(str(inv_total['name']), totals_style_bold),
Paragraph(str(inv_total['value']), cost_style)])
table_style.append(('LINEAFTER', (4, last_item_num), (-2, -1), 0.1, '#707070', None, (2, 2, 2)))
table_style.append(('LINEBELOW', (4, last_item_num), (-1, -2), 0.1, '#707070', None, (2, 2, 2)))
invoice_items_table = Table(data=items_table,
style=table_style)
pdf_story.append(invoice_items_table)
if text_after_invoice_items:
pdf_story.append(Paragraph(text=text_after_invoice_items, style=norm_style))
doc.build(pdf_story, onFirstPage=footer_pagination, onLaterPages=footer_pagination)
After you create the function you will also have to solve dependencies, by adding the following lines at the begginning of the settings.py file:
from reportlab.lib import enums, pagesizes
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Spacer, Paragraph, Table
from django.utils.translation import ugettext_lazy as _
Finally, add the following line after the function that you added earlier and restart fleio, celery and uwsgi services:
PDF_INVOICE_CALLABLE = pdf_invoice_new