Jinja Template with header/footer on every pre-printed form page

Hello, at first I was looking sample print format for the ways to make preprinted forms with big header and footer. But after some research I think I almost reach what I need. Which is for something like this,

The main technique is from here, The Ultimate Print HTML Template with Header & Footer | by Idan Cohen | Medium

Note: If anyone got any other example or tips, please let me know. Thanks!

2 Likes

I will just put my work for Sales Invoice here, in case anyone find it useful.

HTML

<!--Reference: https://medium.com/@Idan_Co/the-ultimate-print-html-template-with-header-footer-568f415f6d2a-->

{% set sales_persons = [] %}
{% for l in doc.sales_team %}
{%   set sales_persons = sales_persons.append(l.sales_person) %}
{% endfor %}

<div class="page-header">
    <!--Left Header -->
    <div id="customer_name" >
        {{ doc.customer_name }}
    </div>
    <div id="address_display" >
        {{ doc.address_display | replace("Thailand", "") | replace("<br>", " ") }}
    </div>
    <div id="tax_id" >
        {{ doc.tax_id }}
    </div>
    <!--Right Header -->
    <div id="doc_name" >
        {{ doc.name }}
    </div>
    <div id="doc_date" >
        {{ frappe.format(doc.posting_date, 'Date') }}
    </div>
    <div id="sales_person" >
        {{ sales_persons|join(', ') }}
    </div>
    <div id="payment_term" >
        {{ doc.payment_terms_template }}
    </div>
    <div id="po_no" >
        {{ doc.po_no }}
    </div>
    <div id="po_date" >
        {{ frappe.format(doc.po_date, 'Date') }}
    </div>
</div>

<div class="page-footer">
    <div id="terms">
        {{ doc.terms }}
    </div>
    <div id="amount_in_text">
        {{ '(' ~ amount_in_bahttext(doc.grand_total) ~ ')' }}
    </div>
    <div id="printed_by">
        {{ frappe.get_fullname(doc.owner) }}
    </div>
    <div id="total">
        {{ frappe.format(doc.total, 'Currency') }}
    </div>
    <div id="discount_amount">
        {{ frappe.format(doc.discount_amount, 'Currency')}}
    </div>
    <div id="net_total">
        {{ frappe.format(doc.net_total, 'Currency')}}
    </div>
    <div id="total_taxes_and_charges">
        {{ frappe.format(doc.total_taxes_and_charges, 'Currency') }}
    </div>
    <div id="grand_total">
        {{ frappe.format(doc.grand_total, 'Currency') }}
    </div>
</div>

<table>

    <thead>
      <tr>
        <td id="col1"><div class="page-header-space"></div></td>
        <td id="col2"><div class="page-header-space"></div></td>
        <td id="col3"><div class="page-header-space"></div></td>
        <td id="col4"><div class="page-header-space"></div></td>
        <td id="col5"><div class="page-header-space"></div></td>
      </tr>
    </thead>
    
    <tbody>
        {% for l in doc.items %}
          <tr>
            <td id="col1">{{ l.qty ~ ' ' ~ l.uom }}</td>
            <td id="col2">{{ l.description }}</td>
            <td id="col3">{{ frappe.format(l.rate, {'fieldtype': 'Currency'}) }}</td>
            <td id="col4">{{ frappe.format(l.discount_percentage, {'fieldtype': 'Percent'}) if l.discount_percentage else '' }}</td>
            <td id="col5">{{ frappe.format(l.amount, {'fieldtype': 'Currency'}) }}</td>
          </tr>
        {% endfor %}
    </tbody>
    
    <tfoot>
      <tr>
        <td><div class="page-footer-space"></div></td>
        <td><div class="page-footer-space"></div></td>
        <td><div class="page-footer-space"></div></td>
        <td><div class="page-footer-space"></div></td>
        <td><div class="page-footer-space"></div></td>
      </tr>
    </tfoot>

</table>

CSS

/* Styles go here */

.print-format {
	padding: 0px;
	margin-right: 0mm;
	margin-left: 0mm;
	margin-top: 0mm;
	margin-bottom: 0mm;
    font-size: 12px;
}
table tr td { font-size: 12px; }

.page-header, .page-header-space {
    height: 230px; /* Max is 230px */
    margin: 0;
}

.page-footer, .page-footer-space {
    height: 230px; /* Max is 230px */
    margin: 0;
}

.page-footer {
  position: fixed;
  bottom: 0;
  width: 100%;
  border-top: 1px solid; /* for demo */
}

.page-header {
  position: fixed;
  top: 0mm;
  width: 100%;
  border-bottom: 1px solid; /* for demo */
}

.page {
  page-break-after: always;
}

@page {
    /* this affects the margin in the printer settings */ 
    margin: 20mm 0mm 30mm 0mm 
}

@media print {
   thead {display: table-header-group;} 
   tfoot {display: table-footer-group;}
}

/* Start of positioning*/
#customer_name {
    position: absolute;
    top: 100px;
    left: 40px;
}
#address_display {
    position: absolute;
    top: 100px;
    left: 55px;
    width: 450px;
    white-space: pre-line;
}
#tax_id {
    position: absolute;
    top: 170px;
    left: 30px;
}
#doc_name {
    position: absolute;
    top: 100px;
    left: 520px;
}
#doc_date {
    position: absolute;
    top: 100px;
    left: 685px;
}
#sales_person {
    position: absolute;
    top: 130px;
    left: 520px;
}
#payment_term {
    position: absolute;
    top: 130px;
    left: 710px;
}
#po_no {
    position: absolute;
    top: 170px;
    left: 550px;
}
#po_date {
    position: absolute;
    top: 170px;
    left: 700px;
}

#col1 {
    width: 158px;
    text-align: right;
    vertical-align: bottom;
    border: solid 1px;
}
#col2 {
    width: 580px;
    text-align: left;
    vertical-align: top;
    border: solid 1px;
}
#col3 {
    width: 105px;
    text-align: right;
    vertical-align: top;
    border: solid 1px;
}
#col4 {
    width: 52px;
    text-align: right;
    vertical-align: top;
    border: solid 1px;
}
#col5 {
    width: 158px;
    text-align: right;
    vertical-align: top;
    border: solid 1px;
}

#terms {
    position: absolute;
    top: 60px;
    left: 30px;
    width: 400px;
    height: 60px;
    overflow:hidden;
    border: 1px solid;
}
#amount_in_text {
    position: absolute;
    top: 140px;
    left: 30px;
    border: 1px solid;
}
#printed_by {
    position: absolute;
    top: 180px;
    left: 560px;
    border: 1px solid;
}

/*Total*/
#total {
    position: absolute;
    top: 0px;
    right: 5px;
}
#discount_amount {
    position: absolute;
    top: 30px;
    right: 5px;
}
#net_total {
    position: absolute;
    top: 60px;
    right: 5px;
}
#total_taxes_and_charges {
    position: absolute;
    top: 90px;
    right: 5px;
}
#grand_total {
    position: absolute;
    top: 120px;
    right: 5px;
}
3 Likes

Also I found that, with big header and footer exceed 230px, Google Chrom overflow the table like there is no theader / tfooter. But in Firefox, it still work fine.

I am not sure how to fix this in Chrome or is this its bug.

1 Like

@kittiu Thank you very much sharing this code. Really appreciatable. I am searching solution for this since a month… This works as It required in excellent way. I have only one problem, like heading Qty,Description,Rate and amount also must show in each page… But presently it is not showing… Can you pls help…

@kittiu found it. It is in the page-header… But now I am having another problem. In PDF the page header is not showing in second page. In html it is showing… pls help me to solve this.