Creating Custom print formats. A walkthrough - Part - 1
Creating Custom print formats. A walkthrough This is an explanation and source for the video on our youtube channel with same title.
Creating Custom print formats. A walkthrough
This is an explanation and source for the video on our youtube channel with same title. ( Link here https://youtu.be/Mn_YHcQz-6M )
1- A simple HTML
<p>Hello </p>
2-
color it
<p style="color:blue;"> Hello </p>
3-Add CSS
CSS
.mycss{
color: blue;
background-color: yellow;
font-style: italic;
}
HTML
<p class="mycss"> Hello </p>
4- Jinja ( template engines )
<p class="mycss"> Now time is : {{ frappe.utils.now()}} </p>
5-Variable on Jinja
{% set greeting = "Hello How are you" %}
<p> {{ greeting }} </p>
6-Condition on Jinja
{% set age = 10 %}
{% if age >= 18 %}
<h1>You are an adult.</h1>
{% else %}
<h1>You are a minor.</h1>
{% endif %}
7- For condition on Jinja
{% set items = ["item1", "item2", "item3", "item4"] %}
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
8- Get something from Frappe
<p>{{frappe.get_doc("Company", doc.company)}}</p>
9- Get invoice number
<p>{{doc.customer_name}}</p>
10-Table
<table border="1">
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
<td>Row 1, Column 3</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
<td>Row 2, Column 3</td>
</tr>
<tr>
<td>Row 3, Column 1</td>
<td>Row 3, Column 2</td>
<td>Row 3, Column 3</td>
</tr>
<tr>
<td>Row 4, Column 1</td>
<td>Row 4, Column 2</td>
<td>Row 4, Column 3</td>
</tr>
</table>
11- COLSPAN and ROWSPAN
<table border="1">
<tr>
<th colspan="2">Column 1 and Column 2</th>
<th>Column 3</th>
</tr>
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
<td>Row 1, Column 3</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
<td>Row 2, Column 3</td>
</tr>
<tr>
<td>Row 3, Column 1</td>
<td>Row 3, Column 2</td>
<td>Row 3, Column 3</td>
</tr>
<tr>
<td>Row 4, Column 1</td>
<td>Row 4, Column 2</td>
<td>Row 4, Column 3</td>
</tr>
</table>
12-
<table border="1">
<tr>
<th colspan="2">Column 1 and Column 2</th>
<th rowspan="2">Column 3</th>
</tr>
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
<td>Row 2, Column 3</td>
</tr>
<tr>
<td>Row 3, Column 1</td>
<td>Row 3, Column 2</td>
<td>Row 3, Column 3</td>
</tr>
<tr>
<td>Row 4, Column 1</td>
<td>Row 4, Column 2</td>
<td>Row 4, Column 3</td>
</tr>
</table>
13- show class usage
<table class=mycss border="1">
14- Put invoice number and customer name
Show how to find it on customize invoice
{{frappe.db.get_value('Customer', doc.customer, 'customer_name')}} - Column 1 and Column 2
{{frappe.db.get_value('Customer', doc.customer, 'tax_id')}} - column 3
invoice number {{doc.name}} row1 column1
15- Macro
{% macro myheader() %}
{% endmacro %}
16 - Show the macro multiple times.
{{ myheader() }}
<p></p>
{{ myheader() }}
<p></p>
{{ myheader() }}
17-Code for the header
{% macro myheader() %}
<table class=mycss border="1">
<tr>
<th colspan="2">{{frappe.db.get_value('Customer', doc.customer, 'customer_name')}}</th>
<th rowspan="2"> {{frappe.db.get_value('Customer', doc.customer, 'tax_id')}} </th>
</tr>
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
<td>Row 2, Column 3</td>
</tr>
<tr>
<td>Row 3, Column 1</td>
<td>Row 3, Column 2</td>
<td>Row 3, Column 3</td>
</tr>
<tr>
<td>Row 4, Column 1</td>
<td>Row 4, Column 2</td>
<td>Row 4, Column 3</td>
</tr>
</table>
{% endmacro %}
18
Make a footer
<table>
<style>
table {
border-collapse: collapse;
border: 1px solid red;
}
th, td {
border: 1px solid red;
padding: 8px;
}
</style>
<tr>
<td colspan="3">Row 1, Column 1 & 2 & 3</td>
<td>Row 1, Column 4</td>
</tr>
<tr>
<td colspan="4">Row 2, Column 1 & 2 & 3 & 4</td>
</tr>
</table>
19-
and Grand total ( row1 column 4 ) and total in numbers ( Row 2, Column 1 & 2 & 3 & 4 )
{{doc.get_formatted("base_total")}} and {{ frappe.utils.money_in_words(doc.base_total)}}
20- Put footer also as macro
{% macro myfooter() %}
{% endmacro %}
{{ myfooter() }}
21- Lets save the footer
{% macro myfooter() %}
<table>
<style>
table {
border-collapse: collapse;
border: 1px solid red;
}
th, td {
border: 1px solid red;
padding: 8px;
}
</style>
<tr>
<td colspan="3">Total Amount</td>
<td>{{doc.get_formatted("base_total")}} </td>
</tr>
<tr>
<td colspan="4"> {{ frappe.utils.money_in_words(doc.base_total)}}</td>
</tr>
</table>
{% endmacro %}
{{ myfooter() }}
22 - Let us build Item table-header
<table class=myitemtablestyle>
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
<tr>
<td>Item</td>
<td>ItemName</td>
<td>Qty</td>
<td>Total</td>
</tr>
</table>
CSS
itemtablestyle
{
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid blue;
padding: 8px;
text-align: left;
font-size: 10px;
}
/* Set width for first and third columns */
colgroup col:first-child,
colgroup col:nth-child(3) {
width: 10%;
}
/* Set width for second column */
colgroup col:nth-child(2) {
width: 60%;
}
----------
23 - Build item table - detail
<table class=itemtablestyle-details>
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
{% set items = ["item1", "item2", "item3", "item4"] %}
{% for item in items %}
<tr>
<td>Item</td>
<td>ItemName</td>
<td>Qty</td>
<td>Total</td>
</tr>
{% endfor %}
</table>
24- put the values.
<table class=itemtablestyle-details>
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
{%- for item in doc.items -%}
<tr>
<td>{{loop.index}}</td>
<td>{{item.item_name}}</td>
<td>{{item.qty }}</td>
<td>{{ item.get_formatted("amount") }}</td>
</tr>
{% endfor %}
</table>
25- Page break
<h1 class="break">Element 1 </h1>
<p>I am on page-1. It will be printed on the first page.</p>
<h1 style="page-break-before: always;"></h1>
<h1 class="break"> Element 2</h1>
<p>I am on page-2. It will be printed on a separate page due to the page-break-before property.</p>
26- Page break on Item list .
<table class="itemtablestyle-details">
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
{%- for item in doc.items -%}
<tr>
<td>{{loop.index}}</td>
<td>{{item.item_name}}</td>
<td>{{item.qty }}</td>
<td>{{ item.get_formatted("amount") }}</td>
</tr>
{% if loop.index % 9 == 0 %}
<tr><td>Break the page here </td></tr>
<tr style="page-break-before: always;"></tr>
{% endif %}
{% endfor %}
</table>
CSS
itemtablestyle-details
{
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid blue;
padding: 8px;
text-align: left;
font-size: 10px;
}
/* Set width for first and third columns */
colgroup col:first-child,
colgroup col:nth-child(3) {
width: 10%;
}
/* Set width for second column */
colgroup col:nth-child(2) {
width: 60%;
}
------------
27- Page numbers .
{% set pages = namespace(current=1) %}
<table class="itemtablestyle-details">
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
{%- for item in doc.items -%}
<tr>
<td>{{loop.index}}</td>
<td>{{item.item_name}}</td>
<td>{{item.qty }}</td>
<td>{{ item.get_formatted("amount") }}</td>
</tr>
{% if loop.index % 9 == 0 %}
<tr><td>Break the page here </td> <td>current page is : {{pages.current}}</td></tr>
<tr style="page-break-before: always;"></tr>
{% set pages.current = pages.current +1 %}
{% endif %}
{% endfor %}
</table>
28- Total pages
{% set pages = namespace(current=1) %}
{% set pagetotal = namespace(total=1) %}
<table class="itemtablestyle-details">
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
{% set pagetotal.total = ((doc.items | length) / 9) | round(0, 'ceil') | int %}
{%- for item in doc.items -%}
<tr>
<td>{{loop.index}}</td>
<td>{{item.item_name}}</td>
<td>{{item.qty }}</td>
<td>{{ item.get_formatted("amount") }}</td>
</tr>
{% if loop.index % 9 == 0 %}
<tr><td>Break the page here </td> <td>current page is : {{pages.current}} / total pages {{pagetotal.total}} : Total items {{(doc.items | length)}}</td></tr>
<tr style="page-break-before: always;"></tr>
{% set pages.current = pages.current +1 %}
{% endif %}
{% endfor %}
<tr><td>Break the page here </td> <td>current page is : {{pages.current}} / total pages {{pagetotal.total}} : Total items {{(doc.items | length)}}</td></tr>
</table>
29-
Now add head on top and footer in the bottom. And this is the final code
{% macro myheader() %}
<table class=headerCSS border="1">
<tr>
<th colspan="2">{{frappe.db.get_value('Customer', doc.customer, 'customer_name')}}</th>
<th rowspan="2"> {{frappe.db.get_value('Customer', doc.customer, 'tax_id')}} </th>
</tr>
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
<td>Row 2, Column 3</td>
</tr>
<tr>
<td>Row 3, Column 1</td>
<td>Row 3, Column 2</td>
<td>Row 3, Column 3</td>
</tr>
<tr>
<td>Row 4, Column 1</td>
<td>Row 4, Column 2</td>
<td>Row 4, Column 3</td>
</tr>
</table>
{% endmacro %}
{% macro myfooter() %}
{% endmacro %}
{{ myfooter() }}
{% macro myfooter() %}
<table class="footerCSS">
<tr>
<td colspan="4">Total Amount</td>
<td>{{doc.get_formatted("base_total")}} </td>
</tr>
<tr>
<td colspan="4"> {{ frappe.utils.money_in_words(doc.base_total)}}</td>
</tr>
</table>
{% endmacro %}
<table class=headerCS border="1">
<tr>{{ myheader() }}<tr>
</table>
{% set pages = namespace(current=1) %}
{% set pagetotal = namespace(total=1) %}
<table class="itemtablestyle-details">
{% set pagetotal.total = ((doc.items | length) / 20) | round(0, 'ceil') | int %}
{%- for item in doc.items -%}
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
<tr>
<td>{{loop.index}}</td>
<td>{{item.item_name}}</td>
<td>{{item.qty }}</td>
<td>{{ item.get_formatted("amount") }}</td>
</tr>
{% if loop.index % 20 == 0 %}
<tr style="page-break-before: always;"> </tr>
{% set pages.current = pages.current +1 %}
<tr><td colspan="4"> {{myheader()}} </td></tr>
<tr><td colspan="4">Break the page here : current page is : {{pages.current}} / total pages {{pagetotal.total}} : Total items {{(doc.items | length)}}</td></tr>
{% endif %}
{% endfor %}
<tr>
<td><img class="qr-code" src="{{doc.ksa_einv_qr}}"></td>
</tr>
</table>
{{ myfooter() }}
CSS
.headerCSS{
color: blue;
background-color: yellow;
font-style: italic;
border-collapse: collapse;
width: 100%;
}
.footercss{
color: white;
background-color: grey;
font-style: italic;
}
itemtablestyle
{
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid blue;
padding: 8px;
text-align: left;
font-size: 10px;
}
/* Set width for first and third columns */
colgroup col:first-child,
colgroup col:nth-child(3) {
width: 20%;
}
/* Set width for second column */
colgroup col:nth-child(2) {
width: 60%;
}
30-
Add a picture. ( QR CODE in this case )
<tr>
<td><img class="qr-code" src="{{doc.ksa_einv_qr}}"></td>
</tr>
Second part in this serial is here https://cloud.erpgulf.com/blog/blogs/print-formats-2.
Let's know you feedback on this, send email to training@ERPGulf.com
Team ERPGulf
The team behind ERPGulf blogs here, expresses their thoughts, shares the experience, often show the frustrations. Contact us on support@ERPGulf.com
Awesome work