New print format for Sales Invoice(develop & v15)


PR Link: feat: create sales invoice print format by iamejaaz · Pull Request #45403 · frappe/erpnext · GitHub

I wanted to know what we can include in this print format and what we can remove. This is a new print format recently merged into the develop and v15 branches of ERPNext. I look forward to your feedback to improve this print format.

4 Likes

I think that’s a great example.

For Türkiye localization:

  • We don’t use “Amount in words” anymore.
  • Dates in the upper right corner usually side by side. Meaning:
    Invoice Date: 01.01.2025
    Instead of
    Invoice Data
    01.01.2025

But this is already great example and can be adjusted manually later on.

2 Likes

A design with a heading like this would cover a lot of the information required on invoices.

Headings can be added by selecting letterhead; that’s why the company logo isn’t added.

Thank you for your efforts in improving the new print format for Sales Invoices! We appreciate the hard work and the recent updates to the develop and v15 branches.

We have a question regarding the alignment of the section. Currently, the total starts immediately after the item table, and if there are only a few items on the current page, there is an empty space between the total and the footer. We would like to know how we can adjust the print format so that the total aligns closer to the footer and reduces any unnecessary space between the total and the footer on the last page.

Looking forward to hearing your thoughts on this. Thanks again!

currently it look like this

how i can achive like below;

ou will need to customize this print format. It is written in Jinja format. You can copy the HTML of this and create a custom new print format. By the way, nice suggestion! I will look into it.

Thanks a lot for considering our suggestion!

Could you kindly provide a starting point or some guidance on how to achieve this? The screenshots we shared earlier were based on the exact HTML from the GitHub repository you mentioned. We’re also using a custom HTML print format, but unfortunately, we haven’t been able to successfully align the totals closer to the footer or eliminate the extra space on the last page.

We experimented with CSS and JavaScript but only saw minimal improvement. For instance, we tried wrapping the item table inside a <div> like this:

<div style="height: 151mm !important; display: table; width: 100%;">

This approach worked partially — only on the first page. When the invoice spans multiple pages, the layout breaks, Also we’re unable to dynamically adjust the height or control the placement of the totals.

If you have any ideas or logic that could help us properly align the total section near the footer — especially in multi-page scenarios — we’d be very grateful. Thanks again for your support and for looking into this!

It may only work when you click the Print button;(Print Preview or the PDF option may have no effect).

{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None, print_heading_template=None) -%}
	{% if letter_head and not no_letterhead %}
		<div class="letter-head">{{ letter_head }}</div>
	{% endif %}
	{% if print_heading_template %}
		{{ frappe.render_template(print_heading_template, {"doc":doc}) }}
	{% else %}
	{% endif %}
	{%- if doc.meta.is_submittable and doc.docstatus==2-%}
	<div class="text-center" document-status="cancelled">
		<h4 style="margin: 0px;">{{ _("CANCELLED") }}</h4>
	</div>
	{%- endif -%}
{%- endmacro -%}
{% for page in layout %}
<div class="page-break">
	<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
		{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
	</div>
	<style>
		.taxes-section .order-taxes.mt-5{
			margin-top: 0px !important;
		}
		.taxes-section .order-taxes .border-btm.pb-5{
			padding-bottom: 0px !important;
		}
		.print-format label{
			color: #74808b;
			font-size: 12px;
			margin-bottom: 4px;
		}

	</style>

	{% if print_settings.repeat_header_footer %}
	<div id="footer-html" class="visible-pdf">
		{% if not no_letterhead and footer %}
		<div class="letter-head-footer">
			{{ footer }}
		</div>
		{% endif %}
		<p class="text-center small page-number visible-pdf">
			{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
		</p>
	</div>
	{% endif %}

	<div style="
		display: flex;
		flex-direction: column;
		min-height: 95vh;
		justify-content: space-between;
	">

		<div>
			<div class="row section-break" style="margin-bottom: 10px;">
				<div class="col-xs-6 p-0">
					<div class="col-xs-12 value text-uppercase"><b>{{ doc.customer }}</b></div>
					<div class="col-xs-12">
						{{ doc.address_display }}
					</div>
					<div class="col-xs-12">
						{{ _("Contact: ")+doc.contact_display if doc.contact_display else '' }}
					</div>
					<div class="col-xs-12">
						{{ _("Mobile: ")+doc.contact_mobile if doc.contact_mobile else '' }}
					</div>
				</div>
				<div class="col-xs-3"></div>
				<div class="col-xs-3" style="padding-left: 5px;">
					<div>
						<div><label>{{ _("Invoice ID") }}</label></div>
						<div>{{ doc.name }}</div>
					</div>
					<div style="margin-top: 20px;">
						<div><label>{{ _("Invoice Date") }}</label></div>
						<div>{{ frappe.utils.format_date(doc.posting_date) }}</div>
					</div>
					<div style="margin-top: 20px;">
						<div><label>{{ _("Due Date") }}</label></div>
						<div>{{ frappe.utils.format_date(doc.due_date) }}</div>
					</div>
				</div>
			</div>
		
			<div class="section-break">
				<table class="table table-bordered table-condensed mb-0" style="width: 100%; border-collapse: collapse; font-size: 12px;">
					<colgroup>
						<col style="width: 5%">
						<col style="width: 45%">
						<col style="width: 10%">
						<col style="width: 20%">
						<col style="width: 20%">
					</colgroup>
					<thead>
						<tr>
							<th class="text-uppercase" style="text-align:center">{{ _("Sr") }}</th>
							<th class="text-uppercase" style="text-align:center">{{ _("Details") }}</th>
							<th class="text-uppercase" style="text-align:center">{{ _("Qty") }}</th>
							<th class="text-uppercase" style="text-align:right">{{ _("Rate") }}</th>
							<th class="text-uppercase" style="text-align:right">{{ _("Amount") }}</th>
						</tr>
					</thead>
					{% for item in doc.items %}
					<tr>
						<td style="text-align:center">{{ loop.index }}</td>
						<td>
							<b>{{ item.item_code }}: {{ item.item_name }}</b>
							{% if (item.description != item.item_name) %}
								<br>{{ item.description }}
							{% endif %}
						</td>
						<td style="text-align: center;">
							{{ item.get_formatted("qty", 0) }}
							{{ item.get_formatted("uom", 0) }}
						</td>
						<td style="text-align: right;">{{ item.get_formatted("net_rate", doc) }}</td>
						<td style="text-align: right;">{{ item.get_formatted("net_amount", doc) }}</td>
					</tr>
					{% endfor %}
				</table>
			</div>
		</div>
	
	
		<div>
	
			<!-- total -->
			<div class="row">
	
				<div class="col-xs-6">
					<div>
						<label>{{ _("Amount in Words") }}</label>
						{{ doc.in_words }}
					</div>
					<div style="margin-top: 20px;">
						<label>{{ _("Payment Status") }}</label>
						{{ doc.status }}
					</div>
				</div>
				<div class="col-xs-6">
					<div class="row section-break">
						<div class="col-xs-7"><div>{{ _("Sub Total") }}</div></div>
						<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("net_total", doc) }}</div>
					</div>
					<div>
						{% for d in doc.taxes %}
							{% if d.tax_amount %}
								<div class="row">
									<div class="col-xs-8"><div>{{ _(d.description) }}</div></div>
									<div class="col-xs-4" style="text-align: right;">{{ d.get_formatted("tax_amount") }}</div>
								</div>
							{% endif %}
						{% endfor %}
					</div>
					<div class="row">
						<div class="col-xs-7"><div>{{ _("Total") }}</div></div>
						<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("grand_total", doc) }}</div>
					</div>
				</div>
	
			</div>
	
	
			<div class="row">
				<div class="col-xs-12">
					<div class="row important data-field">
						<div class="col-xs-12"><label>{{ _("Terms and Conditions") }}: </label></div>
						<div class="col-xs-12">{{ doc.terms if doc.terms else '' }}</div>
					</div>
				</div>
			</div>
		</div>
	</div>

</div>
{% endfor %}

1 Like

Thanks so much for your great support and for sharing the full code — we really appreciate the time and effort!

We tested the code, and it works beautifully for single-page invoices. However, in multi-page invoices, the total section still doesn’t align near the footer like it does on a single page. It seems the layout isn’t maintaining the same structure when the invoice spans more than one page.

Thanks again for all your help so far — we’re getting much closer! If there’s any workaround or adjustment we can try to maintain the same alignment across all pages, we’d be grateful for your input.