Add page break to print format while generating PDF

Any solution for add page break to custom print format which working with print preview but not working with pdf

Hi @Alaa_Badri,

I guess you are looking for this:

<div class="page-break"></div>

Make sure your print format is a “custom format” (otherwise it will not work properly):
grafik

This will add a dotted line where the page break is on the preview:

And create a page break in the pdf…

Hope it helps.

4 Likes

Dear @lasalesi
Thanks for your reply if we check the custom option i’m not able to use builder format just we can add html and css code

Hi @Alaa_Badri,

yes, this is true, the page break feature does not work properly with the builder. If you want this to work, you will have to modify frappe/templates/print_formats/standard.html (not recommended) like this

<!-- <div class="row section-break"> -->
(...)
   <!-- <div class="col-xs-{{ (12 / section.columns|len)|int }} column-break"> -->
   (...)  
   <!-- </div> -->
(...)  
<!-- </div> -->

But again, using a custom print format and coding it in HTML will keep you on the original branch, work for page breaks (and give you a lot of freedom). :wink:

Thanks @lasalesi
could you please give me example how to put something like item table in html code to be like item table in builder

Sure :wink:

<!-- positions -->
<p></p>
 <table  style="width: 100%; ">
 <tr>
    <th style="width: 10%;">{{ _("Pos") }}</th>
    <th style="width: 30%;">{{ _("Description") }}</th>
    <th style="width: 20%;">{{ _("Rate") }}</th>
    <th style="width: 20%;">{{ _("Amount") }}</th>
    <th style="width: 20%;">{{ _("Price") }}</th>
 </tr>
 {% for n in doc.items %}
 <tr>
    <td>{{ loop.index }}</td>
    <td>{{ _(n.item_name) }}</td>
    <td>{{ n.get_formatted('price_list_rate') }}</td>
    <td>{{ n.qty }} {{ _(n.stock_uom) }}</td>
    <td>{{ n.get_formatted('amount') }}</td>
 </tr>
 {% endfor %}
 </table>

<!-- summary and taxes -->
<p></p>
<table style="width: 100%; ">
<tr>
    <td style="width: 60%;"></td>
    <td style="width: 20%;">
	  {{ _("Net amount") }}<br />
	  {% for t in doc.taxes %}
	    {{ _(t.description) }}<br />
	  {% endfor %}
	  {% if doc.discount_amount != 0 %}
	    {{ _("Discount") }}<br />
	  {% endif %}
	  <strong>{{ _("Total") }}</strong>
    </td>
    <td style="width: 20%;">
	  {{ doc.get_formatted('total') }}<br />
	  {% for t in doc.taxes %}
	    {{ t.get_formatted('tax_amount') }}<br />
	  {% endfor %}	  
	  {% if doc.discount_amount != 0 %}
	    -{{ doc.get_formatted('discount_amount') }}<br />
	  {% endif %}
	  <strong>{{ doc.get_formatted('grand_total') }}</strong>
     </td>
</tr>
</table>

Thanks A lot @lasalesi
what about letter head it has to be in the code or we can use system letter head and if it’s in the code how we can repeat it in every page

very appreciate for your help

Use this at the top of your html

<!-- HEAD -->
<div  id="header-html" class="hidden-pdf" >
    {% set letter_head_name = frappe.get_all('Letter Head', filters={'is_default': 1}, fields=['name']) %}
    {% if letter_head_name %}
      {% set letter_head = frappe.get_doc("Letter Head", letter_head_name[0]) %}
      {% if letter_head %}
        {{ letter_head.content }}
      {% else %}
        <p>Defalut letter head not found.</p>
      {% endif %}
    {% else %}
      <p>No default letter head found. Please define a default letter head.</p>
    {% endif %}
</div>

And all the way at the end to create the footer something like

<!-- page footer -->
<div id="footer-html" class="visible-pdf">
   <center><p style="font-size: 8pt !important;">{{ _("Page") }} <span class="page"></span> {{ _("of") }} <span class="topage"></span></p></center>
   {% if letter_head %}
     {{ letter_head.footer }}
   {% else %}
     <p>No letter head found. Please define a letter head under print settings and mark it as default</p>
   {% endif %}</span></p>
</div>

You can also lead a specific letter head (this will try the default).

Hope this helps :wink:

1 Like

Dear @lasalesi thanks a million
:grinning::grinning::grinning::grinning: it’s working fine
but i try to add space between to elements using <p dir="rtl" align="center">&nbsp;</p> working fine in preview but not affected in pdf

You are welcome :wink:

If the print format ignores stuff, sometimes it is due to some “!important” style overrides. However, to add vertical space, have you tried

<p><br></p>

Sorry @lasalesi
but there are another issue about font color only black color appear i using this code td><span style="color: #ff0000 !important;">

Sorry, I am unable to reproduce this…

Preview:
grafik

PDF:
grafik

You might have another issue in the code…

Thanks @lasalesi
it’s working using this code <p style="color:Tomato!important;">

but now i need to add Terms and condition section to my html code do you have any idea
thanks in advance

Hi @Alaa_Badri,

something like this might do the trick

<p>{{ doc.terms }}</p>

Dear @lasalesi
it’s solved by this code

<div class="row section-break">
<div class="col-xs-12 column-break">
    	<div style="padding: 10px 0px" data-fieldname="terms" data-fieldtype="Text Editor">
		{{ doc.terms }}
	</div>
</div>

the other issue is I need to get total of specific item group in summary code, for example, the total of items belongs to product group
if you have any idea about this

thanks in advance

1 Like

Hi @Alaa_Badri,

the optimal way would be to include this as a function somewhere. But, you can also do it directly from Jinja with a code block like this:

<!-- positions -->
<p></p>
 <table  style="width: 100%; ">
 <tr>
    <th style="width: 10%;">Pos</th>
    <th style="width: 30%;">Beschreibung</th>
    <th style="width: 20%;">Preis</th>
    <th style="width: 20%;">Menge</th>
    <th style="width: 20%;">Betrag</th>
 </tr>
 {% set group_total = {} %} 
 {% for n in doc.items %}
   {% if group_total[n.item_group] is defined %}
      {% if group_total.update({n.item_group: group_total[n.item_group] + n.amount}) %}{% endif %}
   {% else %}
      {% if group_total.update({n.item_group: n.amount}) %}{% endif %}
   {% endif %}
 <tr>
    <td>{{ loop.index }}</td>
    <td>{{ n.item_name}}</td>
    <td>{{ n.get_formatted('price_list_rate') }}</td>
    <td>{{ n.qty }} {{ n.stock_uom }}</td>
    <td>{{ n.get_formatted('amount') }}</td>
 </tr>
 {% endfor %}
 </table>

<!-- total per category -->
<table>
  <tr><th>Category</th><th>Total</th></tr>
  {% for key, value in group_total.iteritems() %}
    <tr><td>{{ key }}</td><td>{{ value }}</td>
  {% endfor %}
</table>

<!-- summary and taxes -->
<p></p>
<table style="width: 100%; ">
<tr>
    <td style="width: 60%;"></td>
    <td style="width: 20%;">
	  Summe netto<br />
	  {% for t in doc.taxes %}
	    {{ t.description }}<br />
	  {% endfor %}
	  {% if doc.discount_amount != 0 %}
	    Rabatt<br />
	  {% endif %}
	  <strong>Gesamtbetrag</strong>
    </td>
    <td style="width: 20%;">
	  {{ doc.get_formatted('total') }}<br />
	  {% for t in doc.taxes %}
	    {{ t.get_formatted('tax_amount') }}<br />
	  {% endfor %}	  
	  {% if doc.discount_amount != 0 %}
	    -{{ doc.get_formatted('discount_amount') }}<br />
	  {% endif %}
	  <strong>{{ doc.get_formatted('base_grand_total') }}</strong>
     </td>
</tr>
</table>
1 Like

Dear @lasalesi
first i’m very appreciate your help thanks a lot for that
second if we can isolate every group of items category inside the item table by any break and i try to view item image but it’s not working i got the image file path inside the system ( /files/01700.jpg)

Hi @Alaa_Badri,

you are welcome :wink:

How do you mean isolate every group of items in the item table? If you want to sort for item group, you could build a logic that reads the item_groups (already in your code). Then loop through the item_groups and items and put the items that match the current group (from the group loop) into the table. Something like

{% for key, value in group_total.iteritems() %}
     {% for n in doc.items %}
        {% if n.item_group == key %}
           <!-- add table row here -->
        {% endif %}
     {% endfor %}
     <!-- add item_group total row here -->
{% endfor %}

If your images are not pulled, there might be different reasons. Check this

  • is the image available (take a private browsing window and try to access the link without login) Images used for sales invoices should be “public”
  • is the server able to access the resource (e.g. run $ wget https://yourserver/files/1234.jpg end check if the files is received. This is required for the pdf generation.
  • if the above works, check the html syntax

Hope this helps.

1 Like

Dear @lasalesi
Thanks for your help
could you guide me how to update my code with your code you are suggested above

<!-- positions -->
<p></p>
 <table  style="width: 100%; float: left;" border="1">
 <tr>
    <th style="width: 30%;">Product</th>
    <th style="width: 20%;">Module</th>
    <th style="width: 20%;">QTY</th>
    <th style="width: 20%;">VIS.</th>
    <th style="width: 10%;">Brand</th>
 </tr>
 {% set group_total = {} %} 
 {% for n in doc.items %}
   {% if group_total[n.item_group] is defined %}
      {% if group_total.update({n.item_group: group_total[n.item_group] + n.amount}) %}{% endif %}
   {% else %}
      {% if group_total.update({n.item_group: n.amount}) %}{% endif %}
   {% endif %}
 <tr>
    <td>{{ n.item_name}}</td>
    <td>{{ n.get_formatted('item_group') }}</td>
    <td>{{ n.qty }} {{ n.stock_uom }}</td>
    <td><img src="{{ n.image }}" style="width: 60%"></td>
    <td>{{ n.brand }}</td>
 </tr>
 {% endfor %}
 </table>

<!-- total per category -->
<table>
  <tr><th>Category</th><th>Total</th></tr>
  {% for key, value in group_total.iteritems() %}
    <tr><td>{{ key }}</td><td>{{ value }}</td>
  {% endfor %}
</table>

<!-- summary and taxes -->
<p></p>
<table style="width: 100%; ">
<tr>
    <td style="width: 60%;"></td>
    <td style="width: 20%;">
	  Net Total<br />
	  {% for t in doc.taxes %}
	    {{ t.description }}<br />
	  {% endfor %}
	  {% if doc.discount_amount != 0 %}
	    Rabatt<br />
	  {% endif %}
	  <strong>Grand Total</strong>
    </td>
    <td style="width: 20%;">
	  {{ doc.get_formatted('total') }}<br />
	  {% for t in doc.taxes %}
	    {{ t.get_formatted('tax_amount') }}<br />
	  {% endfor %}	  
	  {% if doc.discount_amount != 0 %}
	    -{{ doc.get_formatted('discount_amount') }}<br />
	  {% endif %}
	  <strong>{{ doc.get_formatted('base_grand_total') }}</strong>
     </td>
</tr>
</table>

Try this :wink:

<!-- positions -->
<p></p>
 <table  style="width: 100%; float: left;" border="1">
 <tr>
    <th style="width: 30%;">Product</th>
    <th style="width: 20%;">Module</th>
    <th style="width: 20%;">QTY</th>
    <th style="width: 20%;">VIS.</th>
    <th style="width: 10%;">Brand</th>
 </tr>
 <!-- prepare groups -->
 {% set group_total = {} %} 
 {% for n in doc.items %}
   {% if group_total[n.item_group] is defined %}
      {% if group_total.update({n.item_group: group_total[n.item_group] + n.amount}) %}{% endif %}
   {% else %}
      {% if group_total.update({n.item_group: n.amount}) %}{% endif %}
   {% endif %}
 {% endfor %}
 {% for key, value in group_total.iteritems() %}
     {% for n in doc.items %}
	    <!-- add group items -->
        {% if n.item_group == key %}
          <tr>
            <td>{{ n.item_name}}</td>
            <td>{{ n.get_formatted('item_group') }}</td>
            <td>{{ n.qty }} {{ n.stock_uom }}</td>
            <td><img src="{{ n.image }}" style="width: 60%"></td>
            <td>{{ n.brand }}</td>
          </tr>
        {% endif %}
     {% endfor %}
	 <!-- total per group -->
     <tr>
        <td>Total</td>
        <td>{{ key }}</td>
        <td></td>
        <td></td>
        <td>{{ value }}</td>
     </tr>	 
 {% endfor %}	 
 </table>

<!-- summary and taxes -->
<p></p>
<table style="width: 100%; ">
<tr>
    <td style="width: 60%;"></td>
    <td style="width: 20%;">
	  Net Total<br />
	  {% for t in doc.taxes %}
	    {{ t.description }}<br />
	  {% endfor %}
	  {% if doc.discount_amount != 0 %}
	    Rabatt<br />
	  {% endif %}
	  <strong>Grand Total</strong>
    </td>
    <td style="width: 20%;">
	  {{ doc.get_formatted('total') }}<br />
	  {% for t in doc.taxes %}
	    {{ t.get_formatted('tax_amount') }}<br />
	  {% endfor %}	  
	  {% if doc.discount_amount != 0 %}
	    -{{ doc.get_formatted('discount_amount') }}<br />
	  {% endif %}
	  <strong>{{ doc.get_formatted('base_grand_total') }}</strong>
     </td>
</tr>
</table>
1 Like