As an alternative to the WYSIWYG invoice layout designer (built into the system), you can use another approach in which an invoice template can be designed as an HTML+CSS file and then uploaded into the system (CSS stands for Cascading Style Sheets). This simplifies the process if the invoice template is created by an external design agency (that does not have access to PortaBilling) and allows advanced template customizations (e.g., arrangement of data to exactly match "legacy" invoices or insertion of dynamic content such as banners) by third-party developers. Also we suggest using this approach for large invoices (hundreds of pages and more) as it requires less computing power for PDF file generation in comparison with the WYSIWYG invoice layout designer.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> [% USE format -%] [% money = format('%.2f') -%] <html> <head> <style type="text/css"> @page { size: A4 portrait; margin-left: 0.25in; margin-right: 0.25in; margin-top: 0.25in; margin-bottom: 0.25in; } @page { @top-left { content: "Page " counter(page) " of " counter(pages); font-family: serif; font-size: 9pt; }; } table { border-collapse: collapse; } body { font-family: serif; } .abs { position: absolute; } .logo { top: 10mm; left: 10mm; } .company { top: 10mm; left: 40mm; width: 50mm; } .invoice { top: 10mm; left: 150mm; font-size: 24pt; font-weight: bold; } .date-num { top: 20mm; left: 120mm; width: 70mm; } .date-num td { border: black solid 1px; font-size: 10pt; text-align: center; vertical-align: center; height: 8mm; } .bill-to { top: 50mm; left: 10mm; width: 80mm; } .bill-to td { border: black solid 1px; padding-left: 3px; } .bill-to-header { height: 10mm; } .bill-to-body { height: 30mm; } .statement { top: 95mm; left: 10mm; width: 130mm; } .statement-header1 { table-column-span: 2; border: black solid 1px; text-align: center; height: 10mm; font-size: 10pt; } .statement-header2 { border: black solid 1px; text-align: center; height: 7mm; font-size: 10pt; } .statement-row { border: black solid 1px; text-align: center; height: 7mm; font-size: 10pt; } .due { top: 120mm; left: 10mm; width: 100mm; } .due .label { width: 70mm; border-bottom: 1px solid black; height: 10mm; font-size: 10pt; } .due .amount { border-bottom: 1px solid black; height: 10mm; font-size: 10pt; text-align: right; } .due .total { font-weight: bold; border-top: 3px solid black; border-bottom: none } .rule { top: 160mm; left: 15mm; width: 160mm; height: 1px; } .rule td { border-top: 1px solid black; } .totals { top: 160mm; left: 80mm; width: 100mm; } .totals .label { width: 75mm; border-bottom: 1px solid black; height: 10mm; font-size: 10pt; } .totals .amount { border-bottom: 1px solid black; height: 10mm; font-size: 10pt; text-align: right; } .totals .total { font-weight: bold; border-top: 3px solid black; border-bottom: none } .totals .subtotal { border-top: 3px solid black; } .details { top: 225mm; left: 10mm; width: 170mm; height: 30mm; border: 1px solid black; font-size: 10pt; text-align: center; vertical-align: top; padding-top: 2mm; } .disclaimer { top: 260mm; left: 10mm; width: 170mm; height: 20mm; border: 1px solid black; font-size: 10pt; text-align: center; vertical-align: top; padding-top: 2mm; } .xdrs { width: 200mm; font-size: 9pt; } .xdrs td { height: 7mm; text-align: left; } </style> </head> <body> <img class="abs logo" src="[% image_path %]parcel.gif"/> <div class="abs company"> [% env.companyname | html %]<br> [% env.addr1 | html %]<br> [% env.addr2 | html %]<br> [% env.addr3 | html %]<br> [% env.addr4 | html %] </div> <div class="abs invoice">Invoice</div> <table class="abs date-num"> <tr> <td>Date</td> <td>Invoice #</td> </tr> <tr> <td>[% invoice.issue_date | html %]</td> <td>[% invoice.invoice_number | html %]</td> </tr> </table> <table class="abs bill-to"> <tr> <td class="bill-to-header">Bill To:</td> </tr> <tr> <td class="bill-to-body"> [% customer.companyname | html %]<br> [% customer.salutation | html %] [% customer.firstname | html %] [% customer.midinit | html %] [% customer.lastname | html %] <br> [% customer.baddr1 %]<br> [% customer.baddr2 %]<br> [% customer.baddr3 %] </td> </tr> </table> <table class="abs statement"> <tr> <td class="statement-header1">Statement Period</td> <td></td> <td></td> </tr> <tr> <td class="statement-header2">From</td> <td class="statement-header2">To</td> <td class="statement-header2">Terms</td> <td class="statement-header2">Due Date</td> </tr> <tr> <td class="statement-row">[% invoice.period_from | html %]</td> <td class="statement-row">[% invoice.period_to | html %]</td> <td class="statement-row">Pay in full</td> <td class="statement-row">[% invoice.due_date | html %]</td> </tr> </table> <table class="abs due"> <tr> <td class="label">Previous Balance</td> <td class="amount">[% money(invoice.previous_balance) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label">Payments</td> <td class="amount">[% money(0 - invoice.payments) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label">Charges this period</td> <td class="amount">[% money(invoice.amount_net - invoice.taxes) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label total">Total due</td> <td class="amount total">[% money(invoice.amount_due) %] [% customer.iso_4217 %]</td> </tr> </table> <table class="abs rule"><tr><td></td></tr></table> <table class="abs totals"> <tr> <td class="label">Session charges</td> <td class="amount">[% money(invoice.calls) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label">Subscription charges</td> <td class="amount">[% money(invoice.subscriptions) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label">Credits</td> <td class="amount">[% money(invoice.manual_charges) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label subtotal">Subtotal</td> <td class="amount subtotal">[% money(invoice.amount_net - invoice.taxes) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label">Taxes</td> <td class="amount">[% money(invoice.taxes) %] [% customer.iso_4217 %]</td> </tr> <tr> <td class="label total">Total</td> <td class="amount total">[% money(invoice.amount_net) %] [% customer.iso_4217 %]</td> </tr> </table> <div class="abs details">Payment Details:<br>Explain how to pay here</div> <div class="abs disclaimer">Fine Print / Disclaimer</div> <div style="page-break-before: always;"></div> <!-- xdrs --> <!-- Payments --> [% SET iter = services.2 -%] [% SET row = iter.next_row -%] [% IF row -%] <table class="xdrs"> <thead> <tr> <td style="table-column-span: 5;"><b>Payments</b></td> </tr> </thead> <tbody> [% SET total_amount = 0 -%] [% WHILE row -%] <tr> <td>[% row.account_id | html %]</td> <td>[% row.destination_description | html %]</td> <td>[% row.xdr_cld | html %]</td> <td>[% row.xdr_connect_time | html %]</td> <td>[% money(row.xdr_charged_amount) %] [% customer.iso_4217 | html %]</td> </tr> [% SET total_amount = total_amount + money(row.xdr_charged_amount) -%] [% row = iter.next_row %] [% END %] </tbody> <tfoot> <tr> <td style="table-column-span: 4;"><b>Total Payments</b></td> <td>[% money(total_amount) %] [% customer.iso_4217 | html %]</td> </tr> </tfoot> </table> [% END -%] <!-- Credits/Adjustments --> [% SET iter = services.1 -%] [% SET row = iter.next_row -%] [% IF row -%] <table class="xdrs"> <thead> <tr> <td style="table-column-span: 5;"><b>Credits</b></td> </tr> </thead> <tbody> [% SET total_amount = 0 -%] [% WHILE row -%] <tr> <td>[% row.account_id | html %]</td> <td>[% row.destination_description | html %]</td> <td>[% row.xdr_cld | html %]</td> <td>[% row.xdr_connect_time | html %]</td> <td>[% money(row.xdr_charged_amount) %] [% customer.iso_4217 | html %]</td> </tr> [% SET total_amount = total_amount + money(row.xdr_charged_amount) -%] [% row = iter.next_row %] [% END %] </tbody> <tfoot> <tr> <td style="table-column-span: 4;"><b>Total Credits</b></td> <td>[% money(total_amount) %] [% customer.iso_4217 | html %]</td> </tr> </tfoot> </table> [% END -%] <!-- Subscriptions --> [% SET iter = services.4 -%] [% SET row = iter.next_row -%] [% IF row -%] <table class="xdrs"> <thead> <tr> <td style="table-column-span: 6;"><b>Subscriptions</b></td> </tr> </thead> <tbody> [% SET total_amount = 0 -%] [% WHILE row -%] <tr> <td style="width: 30mm;">[% row.account_id | html %]</td> <td>[% row.destination_description | html %]</td> <td>[% row.xdr_cld | html %]</td> <td>[% row.xdr_connect_time | html %]</td> <td>[% row.xdr_disconnect_time | html %]</td> <td>[% money(row.xdr_charged_amount) %]</td> </tr> [% SET total_amount = total_amount + money(row.xdr_charged_amount) -%] [% row = iter.next_row -%] [% END -%] </tbody> <tfoot> <tr> <td style="table-column-span: 5;"><b>Total Subscriptions</b></td> <td><b>[% money(total_amount) %]</b></td> </tr> </tfoot> </table> [% END -%] <!-- Taxes --> [% SET iter = services.12 -%] [% SET row = iter.next_row -%] [% IF row -%] <table class="xdrs"> <thead> <tr> <td style="table-column-span: 6;"><b>Taxes</b></td> </tr> </thead> <tbody> [% SET total_amount = 0 -%] [% WHILE row -%] <tr> <td>[% row.account_id | html %]</td> <td>[% row.destination_description | html %]</td> <td>[% row.xdr_cld | html %]</td> <td>[% row.xdr_connect_time | html %]</td> <td>[% row.xdr_disconnect_time | html %]</td> <td>[% money(row.xdr_charged_amount) %] [% customer.iso_4217 | html %]</td> </tr> [% SET total_amount = total_amount + money(row.xdr_charged_amount) -%] [% row = iter.next_row %] [% END %] </tbody> <tfoot> <tr> <td style="table-column-span: 5;"><b>Total Taxes</b></td> <td>[% money(total_amount) %] [% customer.iso_4217 | html %]</td> </tr> </tfoot> </table> [% END -%] <!-- all other... 3, 4, 6, 7, 8, 9, 10, 14, 15 --> [% FOREACH srv = services -%] [% NEXT UNLESS srv -%] [% SET i_service = srv.i_service -%] [% NEXT IF i_service == 1 -%] [% NEXT IF i_service == 2 -%] [% NEXT IF i_service == 11 -%] [% NEXT IF i_service == 12 -%] [% NEXT IF i_service == 100 -%] [% SET row = srv.next_row -%] [% IF row -%] <table class="xdrs"> <thead> <tr> <td style="table-column-span: 7;"><b>[% srv.name | html %]</b></td> </tr> </thead> <tbody> [% SET total_amount = 0 -%] [% SET total_time = 0 -%] [% SET prev_row = {} -%] [% WHILE row -%] [% IF prev_row AND prev_row.country_name != row.country_name -%] <tr> <td style="table-column-span: 5;"><b>Total by [% prev_row.country_name | html -%]</b></td> <td>[% total_time %]</td> <td>[% total_amount %] [% customer.iso_4217 %]</td> </tr> <tr> <td style="table-column-span: 7;">[% row.country_name | html -%]</td> </tr> [% SET total_amount = 0 -%] [% SET total_time = 0 -%] [% END -%] <tr> <td>[% row.xdr_cli | html %]</td> <td>[% row.xdr_cld | html %]</td> <td>[% row.country_name | html -%]</td> <td>[% row.destination_description | html %]</td> <td>[% row.xdr_connect_time | html %]</td> <td>[% row.xdr_charged_quantity %]</td> <td>[% money(row.xdr_charged_amount) %] [% customer.iso_4217 | html %]</td> </tr> [% SET total_amount = total_amount + money(row.xdr_charged_amount) -%] [% SET total_time = total_time + row.xdr_charged_quantity -%] [% SET prev_row = row -%] [% SET row = srv.next_row -%] [% END -%] [% IF prev_row -%] [% END -%] [% IF prev_row -%] <tr> <td style="table-column-span: 5;"><b>Total by [% prev_row.country_name | html -%]</b></td> <td>[% total_time %]</td> <td>[% total_amount %] [% customer.iso_4217 %]</td> </tr> [% END -%] </tbody> </table> [% END -%] [% END -%] </body> </html>