PDF generation advanced configuration

In order to redefine the PDF layout you must create a new function in /var/webapps/fleio/project/fleio/settings.py. 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.

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