Hello everyone,
I have been trying to build a custom print format for invoices. I was able to get most of the requirements needed but I find the result to be somewhat complex and I would like to know if there is a simpler way of doing it.
It needs to be saved as a pdf, have a specific layout and some rules based on pagination.
Most of my problems come from the pagination rules, I need access to pagination on the header and on the footer inside the templating engine, so i can display information based on the current page and this does not seem to be possible using the built in pagination that come from css counters.
I was able to do it by creating my own pagination inside a loop but even then I needed to create my own header and footer so it can be accessible inside.
The code bellow is my current implementation, the information is mostly static to make it easy to replicate.
I am using inline styles since i started with the print format builder beta, before moving to custom format, and for some reason the html blocks do not accept a style block or access the custom css in the format (maybe its a bug, im looking into it and will open an issue if needed).
I am using a letter head header / footer but they do not have any dynamic data.
I will keep the topic updated with more information if needed.
Thank you,
João
HTML
{% set items = [{name: 'item 1'}, {name: 'item 2'}, {name: 'item 3'}, {name: 'item 4'}, {name: 'item 5'}, {name: 'item 6'}, {name: 'item 7'}, {name: 'item 8'}, {name: 'item 9'}, {name: 'item 10'}, {name: 'item 11'}, {name: 'item 12'}, {name: 'item 13'}, {name: 'item 14'}, {name: 'item 15'},
{name: 'item 16'}, {name: 'item 17'}, {name: 'item 18'}, {name: 'item 19'}, {name: 'item 20'}, {name: 'item 21'}, {name: 'item 22'}, {name: 'item 23'}, {name: 'item 24'}, {name: 'item 25'}, {name: 'item 26'}, {name: 'item 27'}, {name: 'item 28'}, {name: 'item 29'}, {name: 'item 30'}] %}
{% set itemsPerPage = namespace(value=11) %}
{% set pages = namespace(current=1) %}
{% set pagetotal = namespace(total=1) %}
{% set pagetotal.total = ((items | length) / itemsPerPage.value) | round(0, 'ceil') | int %}
{% macro myHeader() %}
<div id="wia-main-header">
<div class="letter-head">
{{ letter_head }}
</div>
<div>
<table style="
font-size: 11px;
width: 100%;
border-spacing: 0;
margin-top: 25px;
margin-bottom: 2px;
border-collapse: collapse;
">
<tbody>
<tr>
<th style="padding: 0; width: 163.33px"></th>
<th style="padding: 0; width: 91.78px"></th>
<th style="padding: 0; width: 51.88px; margin-right: 10px">Pág.</th>
<th style="padding: 0; width: 175px">Condição de Pagamento</th>
<th style="padding: 0"></th>
<th style="padding: 0; width: 95px">Data Venc.</th>
<th style="padding: 0; width: 95px">Data Doc.</th>
</tr>
<tr>
<td style="border: 1px solid black; padding: 5px 10px; text-align: center;">Original - 2ª Via</td>
<td style="border: 1px solid black; padding: 5px 10px; border: none"></td>
<td style="border: 1px solid black; padding: 5px 10px; text-align: center;">{{pages.current}} / {{pagetotal.total}}</td>
<td style="border: 1px solid black; padding: 5px 10px; text-align: center;">PRONTO PAG.</td>
<td style="border: 1px solid black; padding: 5px 10px; border: none"></td>
<td style="border: 1px solid black; padding: 5px 10px; text-align: center;">2023-06-06</td>
<td style="border: 1px solid black; padding: 5px 10px; text-align: center;">{{doc.due_date}}</td>
</tr>
</tbody>
</table>
<table style="font-size: 11px; border-collapse: collapse; width: 100%">
<tbody>
<tr>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Nº de Contribuinte
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Cliente Nº
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Desc.
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Requisição
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
<div style="
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
">
<span>Moeda</span>
<span>Câmbio</span>
</div>
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Vendedor
</th>
<th style="border: 1px solid black; text-align: center; padding: 2px">
Fatura Simplificada
</th>
</tr>
<tr>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px;
">
99999999
</td>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px;
">99999999</td>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px 2px;
">
0,00
</td>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px 2px;
"></td>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px;
">
<div style="
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
">
<span>EUR</span><span>1,000000</span>
</div>
</td>
<td style="
border: 1px solid black;
text-align: center;
padding: 4px;
">
999
</td>
<td style="
border: 1px solid black;
text-align: center;
text-align: right;
padding: 4px;
">
ABC 9999999
</td>
</tr>
</tbody>
</table>
</div>
</div>
{% endmacro %}
{% macro myFooter() %}
<div id="bar">
<div class="letter-head-footer">
<div style="border-top: 1px solid black; width: 100%;">
<p style="font-size: 8px; margin: 4px 0">
foobar - Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<div style="height: 35px">
<p style="margin: 0; font-size: 9px; font-weight: bold">Obs.:</p>
</div>
<div style="display:-webkit-box; display:-ms-flexbox; display:flex; height: 30px; width: 100%;">
<div style="width: 32%">
<p style="margin: 0; font-size: 9px">Local de carga:</p>
</div>
<div style="width: 68%">
<p style="margin: 0; font-size: 9px">Local de descarga:</p>
</div>
</div>
<div style="display:-webkit-box; display:-ms-flexbox; display:flex; width: 100%;">
<div style="width: 35%; padding-right: 7.5px;">
<p style="margin: 0; font-size: 9px">Meio de Expedição:</p>
</div>
<div style="width: 25%; padding: 0 7.5px;">
<p style="margin: 0; font-size: 9px">
Data/Hora: 2023-06-06 / 13:33
</p>
</div>
<div style="width: 40%; padding-left: 7.5px;">
<p style="margin: 0; font-size: 9px">Viatura:</p>
</div>
</div>
<div style="display:-webkit-box; display:-ms-flexbox; display:flex; width: 100%;">
<div style="width: 35%; padding-right: 7.5px;">
</div>
<div style="width: 25%; padding: 0 7.5px;">
<p style="margin: 4px 0 0 0; font-size: 9px">
ABC:99999999999
</p>
</div>
<div style="width: 40%; padding-left: 7.5px;">
</div>
</div>
<div style="display:-webkit-box; display:-ms-flexbox; display:flex; width: 100%;">
<div style="
width: 35%;
padding-right: 7.5px;
">
<p style="margin: 0; font-size: 9px; font-weight: bold">
Resumo de Impostos
</p>
<table
style="
width: 100%;
font-size: 9px;
border-collapse: collapse;
border-spacing: 0;
border: 1px solid black;
"
>
<tr>
<th style="text-align:center; border: 1px solid black">Descrição</th>
<th style="text-align:center; border: 1px solid black">Taxa</th>
<th style="text-align:center; border: 1px solid black">Incidência</th>
<th style="text-align:center; border: 1px solid black">Imposto</th>
<th style="text-align:center; border: 1px solid black">Retenção</th>
</tr>
<tr>
<td style="border-top: none; border-bottom: none">IVA</td>
<td style="border-top: none; border-bottom: none">6,00</td>
<td style="border-top: none; border-bottom: none">181,90</td>
<td style="border-top: none; border-bottom: none">10,90</td>
<td style="border-top: none; border-bottom: none"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none">IVA</td>
<td style="border-top: none; border-bottom: none">23,00</td>
<td style="border-top: none; border-bottom: none">163,79</td>
<td style="border-top: none; border-bottom: none">37,66</td>
<td style="border-top: none; border-bottom: none"></td>
</tr>
</table>
</div>
<div style="width: 25%; padding: 0 7.5px;">
<div
style="
margin: 5px 0 0 0;
width: 100%;
height: 150px;
background-color: darkgrey;
"
></div>
</div>
<div style="width: 40%; padding-left: 7.5px;">
<div style="border: 1px solid black;">
<div
style="
border-bottom: 1px solid black;
font-size: 9px;
display:-webkit-box;
display:-ms-flexbox;
display:flex;
"
>
<div style="width: 50%; text-align: right">
<div>Mercadoria:</div>
<div>Descontos:</div>
<div>Outros:</div>
<div style="height: 75px">Acertos:</div>
</div>
</div>
<div style="font-size: 9px; display:-webkit-box;display:-ms-flexbox;display:flex;">
<div style="width: 50%; text-align: right">
<div style="font-weight: bold; font-size: 10px">
Total do Documento: {{pages.current}}
</div>
<div>Retenções:</div>
<div style="font-size: 10px">Total a Pagar:</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{ footer }}
</div>
</div>
{% endmacro %}
<div>
<div class="wia-tbl">
{{myHeader()}}
<div class="wia-row">
<div class="wia-cell wia-cell-header">ID</div>
<div class="wia-cell wia-cell-header">Nr</div>
<div class="wia-cell wia-cell-header">Header 1</div>
<div class="wia-cell wia-cell-header">Header 2</div>
<div class="wia-cell wia-cell-header">Header 2</div>
</div>
{%- for item in items -%}
<div class="wia-row">
<div class="wia-cell">{{loop.index}}</div>
<div class="wia-cell">{{item[name]}}</div>
<div class="wia-cell">{{pages.current}}</div>
<div class="wia-cell">{{pages.current}}</div>
<div class="wia-cell">{{pages.current}}</div>
</div>
{% if loop.index % itemsPerPage.value == 0 %}
{{ myFooter() }}
<div class="page-break"></div>
{% set pages.current = pages.current +1 %}
{{ myHeader() }}
<div class="wia-row">
<div class="wia-cell wia-cell-header">ID</div>
<div class="wia-cell wia-cell-header">Nr</div>
<div class="wia-cell wia-cell-header">Header 1</div>
<div class="wia-cell wia-cell-header">Header 2</div>
<div class="wia-cell wia-cell-header">Header 2</div>
</div>
{% endif %}
{% endfor %}
{{ myFooter() }}
</div>
</div>
Custom css
#wia-main-header {
/*margin-top: 10mm;*/
padding-top: 10mm;
margin-bottom: 5px;
/*background: green;*/
}
.print-format {
padding-left: 10mm;
padding-right: 10mm;
padding-top: 0mm;
margin-top: 0mm;
margin-left: 0mm;
margin-right: 0mm;
}
.page-break {
page-break-before: always;
}
.wia-tbl {
width: 190mm;
font-size: 9pt;
}
.wia-row {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.wia-cell-header {
border: 1px solid black;
border-right: 0;
text-align: center;
}
.wia-cell-header:last-child {
border-right: 1px solid black;
}
.wia-cell {
width: 55mm;
padding: 2px;
box-sizing: border-box;
}
.wia-cell:nth-child(1) {
width: 11mm;
}
.wia-cell:nth-child(2) {
width: 15mm;
}