For Zoho Services only:


I'm actually part of something bigger at Ascent Business Solutions recognized as the top Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions offer support for smaller technical fixes and projects for larger developments, such as migrating to a ZohoCRM.  A team rather than a one-man-band is always available to ensure seamless progress and address any concerns. You'll find our competitive support rates with flexible, no-expiration bundles at https://ascentbusiness.co.uk/zoho-services/uk-zoho-support.  For larger projects, talk to our experts and receive dedicated support from our hands-on project consultants at https://ascentbusiness.co.uk/zoho-services/zoho-crm-implementation.

The team I manage specializes in coding API integrations between Zoho and third-party finance/commerce suites such as Xero, Shopify, WooCommerce, and eBay; to name but a few.  Our passion lies in creating innovative solutions where others have fallen short as well as working with new businesses, new sectors, and new ideas.  Our success is measured by the growth and ROI we deliver for clients, such as transforming a garden shed hobby into a 250k monthly turnover operation or generating a +60% return in just three days after launch through online payments and a streamlined e-commerce solution, replacing a paper-based system.

If you're looking for a partner who can help you drive growth and success, we'd love to work with you.  You can reach out to us on 0121 392 8140 (UK) or info@ascentbusiness.co.uk.  You can also visit our website at https://ascentbusiness.co.uk.

Zoho Creator: Render to PDF with margins and page numbers

What?
There are already articles out there that document this but I use this more and more and would rather just find it on my site than going through multiple bookmarks.

Why?
This use-case is for a customer who simply wanted a quote template to be rendered for PDF or print format. I have another article for a different client who wants a pretty advanced HTML template (well it's a HTML table with rowspans and the borders need to merge cells). But here we're simply going to use ZohoCreator and its PDF rendering options.

What's wrong with just using CSS? It looks beautiful on ZohoCreator pages but then you hit the PDF button and it pretty much vomits your output back to the 90s. The code snippet below is just for a standard template with a modern design.

How?
Ok, admittedly, when first designing the template, there could have been a fair few improvements right out of the gate. Then the client comes back and says things like "can we add this?", "can it say this instead?", "can it be in a bigger font?"... without pushing back too much, it can end up looking like those old websites with HTML tables. We're going to use a HTML table again but that's because I want to send it to the PDF renderer in ZohoCreator and want all the data to stay aligned. Using only DIV layers can cause a few unexpected layouts in ZohoCreator.

The Quick Answer: the page margins
Tackling the page margin on render, I wrap the whole lot inside a DIV layer with the key attribute zcpage-spacing:
copyraw
<div id="my-container" zcpage-spacing="40">

	<div id="my-header"  zcpage-headerhtml="true">
		...
	</div>
	

	<div id="my-body">
		...
	</div>


	<div id="my-footer"  zcpage-footerhtml="true">
		...
	</div>

</div>
  1.  <div id="my-container" zcpage-spacing="40"> 
  2.   
  3.      <div id="my-header"  zcpage-headerhtml="true"> 
  4.          ... 
  5.      </div> 
  6.   
  7.   
  8.      <div id="my-body"> 
  9.          ... 
  10.      </div> 
  11.   
  12.   
  13.      <div id="my-footer"  zcpage-footerhtml="true"> 
  14.          ... 
  15.      </div> 
  16.   
  17.  </div> 

The Quick Answer: the header
This repeats the header on every page. The key here is the HTML attribute zcpage-headerhtml:
copyraw
<div id="my-header" zcpage-headerhtml="true">
	<table class="my-table">
		<tbody>
			<tr>
				<td><img src="/<%=v_LogoPhotoUrl%>" width="150" height="auto" alt="My Company" /></td>
				<td>My Company Ltd<br />Test Street,<br />Test County TEST1 ZIP1<br /><br />+44 (0)1234 567890<br />This email address is being protected from spambots. You need JavaScript enabled to view it.</td>
				<td>QUOTE</td>
			</tr>
		</tbody>
	</table>	
	<hr />
	<br />
</div>
  1.  <div id="my-header" zcpage-headerhtml="true"> 
  2.      <table class="my-table"> 
  3.          <tbody> 
  4.              <tr> 
  5.                  <td><img src="<%=v_LogoPhotoUrl%>" width="150" height="auto" alt="My Company" /></td> 
  6.                  <td>My Company Ltd<br />Test Street,<br />Test County TEST1 ZIP1<br /><br />+44 (0)1234 567890<br />This email address is being protected from spambots. You need JavaScript enabled to view it.</td> 
  7.                  <td>QUOTE</td> 
  8.              </tr> 
  9.          </tbody> 
  10.      </table> 
  11.      <hr /> 
  12.      <br /> 
  13.  </div> 

The Quick Answer: the footer
This repeats the footer on every page. The key point is the HTML attribute zcpage-footerhtml; but note the 3 other CSS classes zcpage-pagenumber, currentPageNumber, and totalPageNumber which will output the current page number and total count of pages respectively:
copyraw
<div id="my-footer" zcpage-footerhtml="true" class="align-center">
	<hr />
	<br />
	<div class="zcpage-pagenumber">
		Page <span class="currentPageNumber"> </span> of <span class="totalPageNumber"> </span><br />
	</div>
	<div class="footer-copyright">
		Copyright &copy; <%=v_ThisYear%> My Company Ltd. All Rights Reserved.
	</div>
</div>
  1.  <div id="my-footer" zcpage-footerhtml="true" class="align-center"> 
  2.      <hr /> 
  3.      <br /> 
  4.      <div class="zcpage-pagenumber"> 
  5.          Page <span class="currentPageNumber"> </span> of <span class="totalPageNumber"> </span><br /> 
  6.      </div> 
  7.      <div class="footer-copyright"> 
  8.          Copyright &copy; <%=v_ThisYear%> My Company Ltd. All Rights Reserved. 
  9.      </div> 
  10.  </div> 

In for a penny, in for a pound
Here's my full template deluge/html/css code to put in a Zoho Creator page excluding the content (edited for Public display - Not the used final version):
copyraw
<%{
	/* *******************************************************************************
	Function:       -
	Label:          Zoho Creator Page: Print_Quote
	Trigger:        This is the HTML snippet for the "Print Quote" page
	Purpose:		The below code is the HTML combined with Deluge to output a quote in this template.
	Inputs:         p_Quotes [collection of the quote form - when tick multiple records in a list]
	Outputs:        A HTML page that can be rendered in PDF or Print

	Date Created:   2023-04-03 (Joel Lipman)
					- Initial release
	Date Modified:	???
					- ???

	More Information:
					https://help.zoho.com/portal/en/kb/creator/developer-guide/others/url-patterns/articles/functionality-based-urls#To_convert_Page_to_PDF
					https://help.zoho.com/portal/en/kb/creator/developer-guide/pages/create-and-manage-pages/articles/guidelines-for-exporting-page-into-pdf#31_PDF_Margin

	******************************************************************************* */	
	//
	// the publish key of the product images report
	v_ProductPhoto_PublishKey = "AAAAABBBBBCCCCDDDDEEEFFFGGGGHHHIIIJJJKKLLLMMMNNOOOPPPQQWRRRSSTTUUVVWWXXYYZZ122345567890";
	// my company logo (must be a public URL to render in the PDF)
	v_LogoPhotoUrl = "https://mycompany.com/assets/img/template_logo.png";
	// getting current year
	v_ThisYear = zoho.currentdate.getYear();
	// converting the submitted quote record IDs as a list
	l_QuoteIDs = ifnull(input.p_Quotes,"").toList();
	//
	// get customer information
	v_CurrencyHtmlEntity = "£";
	v_Firstname = "";
	v_Lastname = "";
	v_Email = "";
	v_QuoteDate = zoho.currentdate.toString("dd-MMM-yyyy");
	v_AddedBy = "The No. 1 Salesperson";
	for each  v_QuoteID in l_QuoteIDs
	{
		c_Quote = Quote[ID == v_QuoteID];
		if(c_Quote.count() > 0)
		{
			if(c_Quote.Name != null)
			{
				v_Firstname = c_Quote.Name.first_name;
				v_Lastname = c_Quote.Name.last_name;
			}
			if(c_Quote.Email != null)
			{
				v_Email = c_Quote.Email;
			}
			v_QuoteDate = c_Quote.Modified_Time.toString("dd-MMM-yyyy");
			c_AddedBy = Staff[Zoho_Creator_User == zoho.loginuser];
			if(c_AddedBy.count() > 0)
			{
				v_AddedBy = c_AddedBy.Name;
			}
			break;
		}
	}
	%>
<style>
		/* page sizing */
		@page {
			size: A4;
			margin: 0; 
		}
		
		/* CSS for when opting to print rather than render in PDF */
		/* Overridden by zcpage-pagenumber which hides the entire div when printing */
		.currentPageNumber {
			content: "1";
		}
		.totalPageNumber {
			content: "1";
		}		

		/* globals */
		#my-header td,
		#my-container th,
		#my-container td,
		#my-footer{
			color: #111;
			font-family: 'Lato', sans-serif !important;
			font-size: 10pt;
			font-weight:300;
		}
		.my-table{
			width: 100%;
			margin-bottom:20px;
		}
		.my-table td,
		.my-table th {
			padding: 5px 10px;
		}
		
		/* letterhead header */
		#my-header .my-table thead{
			margin-bottom:30px;
		}		
		#my-header .my-table tr td:first-child{
			width: 175px;
		}
		#my-header .my-table tr td:nth-child(2){
			font-size: 7pt;
			text-transform: uppercase;
		}
		#my-header .my-table tr td:nth-child(3){
			text-transform: uppercase;
			font-size: 36pt;
			font-weight: 100;
			color: #777;
			text-align: right;
			vertical-align: top;
		}
		
		/* main content */
		#my-content table:first-child td{
			vertical-align:top;
		}
		#my-content table:first-child label{
			text-transform: uppercase;
			color: #777;
			display:inline-block;
			width:100px;
		}
		#my-content table:first-child span{
			font-weight:400;
			display:inline-block;
			min-width:100px;
		}
		#my-content table:first-child tr td:nth-child(2){
			width: 240px;
		}

		/* line items */
		#my-line-items thead tr th{
			color: #777;
			text-align:right;
			text-transform: uppercase;
			vertical-align: bottom;
			border-bottom: 1px solid #eee;
		}
		#my-line-items thead tr th:first-child{
			width: 50%;
			text-align:left;
		}
		#my-line-items tbody tr td img{
			width: 75px;
			height: auto;
			max-height: 75px;
			max-width: 75px;
			margin-right: 15px;
			float:left;
		}
		#my-line-items tbody tr td{
			font-weight:400;
			text-align:right;
			vertical-align:top;
			padding: 0 10px 10px 10px;
		}
		#my-line-items tbody tr td span{
			font-size: 8pt;
		}
		#my-line-items tbody tr td span:first-child{
			font-size: 10pt;
		}
		#my-line-items tbody tr td:first-child{
			text-align:left;
		}
		#my-line-items tbody tr td:first-child div{
			float:left;
			display:inline-block;
		}
		#my-line-items tfoot tr th{
			color: #777;
			text-align:right;
			text-transform: uppercase;
			vertical-align: bottom;
		}
		#my-line-items tfoot tr:nth-child(2) th{
			color: #111;
			font-weight:400;
			border-top: 1px solid #eee;
		}
		
		/* customer notes */
		#my-content table:nth-child(3) p{
			font-size:7pt;
		}
		
		/* page footer */
		#my-footer{
			text-align:center;
			font-size:7pt;
			text-transform: uppercase;
		}
		#my-container hr{
			border: 0;
			height: 1px;
			background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
		}
	</style>

<div id="my-container" zcpage-spacing="40">

	<div id="my-header" zcpage-headerhtml="true">
		<table class="my-table">
			<tbody>
				<tr>
					<td><img src="/<%=v_LogoPhotoUrl%>" width="150" height="auto" alt="My Company" /></td>
					<td>My Company Ltd<br />Test Street,<br />Test County TEST1 ZIP1<br /><br />+44 (0)1234 567890<br  />info@mycompany.com<br  />www.mycompany.com<br  />
					<td>QUOTE</td>
				</tr>
			</tbody>
		</table>	
		<hr />
		<br />
	</div> 

	<div id="my-content">
		<table class="my-table">
			<thead>
				<tr>
					<td>
						<label>Quote For:</label><br />
						<span><%=v_Firstname%> <%=v_Lastname%></span><br />
						<span><%=v_Email%></span>
					</td>
					<td>
						<label>Quote Date:</label> <span><%=v_QuoteDate%></span><br />
						<label>Quote By:</label> <span><%=v_AddedBy%></span><br />
					</td>
				</tr>
			</thead>
		</table>
		
		<table class="my-table" id="my-line-items">
			<thead>
				<tr>
					<th>Item Description</th>
					<th>Quantity</th>
					<th>Unit Price</th>
					<th>Unit Discount</th>
				</tr>
			</thead>
			<tbody>
<%
	v_TotalQuantity = 0;
	v_TotalPrice = 0.00;
	v_TotalDiscount = 0.00;
	v_TotalAmount = 0.00;
	for each  v_QuoteID in l_QuoteIDs
	{
		c_Quote = Quote[ID == v_QuoteID];
		if(c_Quote.count() > 0)
		{
			v_LineItemTotal = 0.00;
			v_ThisQuantity = ifnull(c_Quote.Quantity,1).toLong();
			v_TotalQuantity = v_TotalQuantity + v_ThisQuantity;
			//
			v_PriceDisplay = if(c_Quote.Unit_Price && c_Quote.Unit_Price > 0,v_CurrencyHtmlEntity + c_Quote.Unit_Price.round(2),"-");
			v_PriceValue = if(c_Quote.Unit_Price && c_Quote.Unit_Price > 0,c_Quote.Unit_Price,0);
			v_PriceTotal = v_PriceValue * v_ThisQuantity;
			v_TotalPrice = (v_TotalPrice + v_PriceTotal).round(2);
			//
			v_DiscountDisplay = if(c_Quote.Discount_Amount && c_Quote.Discount_Amount > 0,v_CurrencyHtmlEntity + c_Quote.Discount_Amount.round(2),"-");
			v_DiscountValue = if(c_Quote.Discount_Amount && c_Quote.Discount_Amount > 0,c_Quote.Discount_Amount,0);
			v_DiscountTotal = v_DiscountValue * v_ThisQuantity;
			v_TotalDiscount = (v_TotalDiscount + v_DiscountTotal).round(2);
			//
			v_LineItemTotal = (v_PriceTotal - v_DiscountTotal).round(2);
			v_TotalAmount = (v_TotalAmount + v_LineItemTotal).round(2);
			//
			//
			// product photo
			c_Product = Products[ID == c_Quote.Product];
			if(c_Product.count() > 0)
			{
				v_ProductPhotoFile = c_Model.Product.getSuffix("http").getPrefix("\"").getSuffix("/image/");
				v_ProductPhotoURL = "https://creatorexport.zoho.com/file" + zoho.appuri + "Products/" + c_Product.ID + "/Photo/image-download/" + v_ProductPhoto_PublishKey + "?filepath=" + v_ProductPhotoFile;
			}
			v_ItemImgDisp = "<img src='" + v_ProductPhotoURL + "' alt='" + c_Quote.Product.Product_Name + "' />";
			//
			// item and description
			v_ItemDescDisp = "<span>" + c_Quote.Product.Product_Name + "</span><br />";
			if(c_Quote.Special_Edition.containsIgnoreCase("yes"))
			{
				v_ItemDescDisp = "<span>SPECIAL EDITION</span><br />";
			}
			//
			// other properties to add to the description
			v_ItemDescDisp = v_ItemDescDisp + "<span>Awesome Reason To Buy This #1</span><br />";
			v_ItemDescDisp = v_ItemDescDisp + "<span>Awesome Reason To Buy This #2</span><br />";
			//
			// condition grade
			v_ConditionChar = ifnull(c_Quote.Grade_Condition,"C").subString(0,1);
			v_ConditionDisp = if(v_ConditionChar == "N","New",v_ConditionChar + " Grade");
			v_ItemDescDisp = v_ItemDescDisp + "<span>" + v_ConditionDisp + "</span><br />";
			//
			// unique item reference
			if(c_Quote.Quote_Reference.contains("Q-"))
			{
				v_ItemDescDisp = v_ItemDescDisp + "<span><i>Ref. " + c_Quote.Quote_Reference + "</i></span><br />";
			}
			%>
				<tr>
					<td><%=v_ItemImgDisp%><div><%=v_ItemDescDisp%></div></td>
					<td><%=v_ThisQuantity%></td>
					<td><%=v_PriceDisplay%></td>
					<td><%=v_DiscountDisplay%></td>
					<td><%=v_CurrencyHtmlEntity><%=v_LineItemTotal%></td>
				</tr>
<%
		}
	}
	%>
</tbody>
			<tfoot>
				<tr>
					<th></th>
					<th>Total Quantity</th>
					<th>Total Price</th>
					<th>Total Discount</th>
					<th>Total Amount</th>
				</tr>
				<tr>
					<th></th>
					<th><%=v_TotalQuantity%></th>
					<th><%=v_CurrencyHtmlEntity><%=v_TotalPrice%></th>
					<th><%=v_CurrencyHtmlEntity><%=v_TotalDiscount%></th>
					<th><%=v_CurrencyHtmlEntity><%=v_TotalAmount%></th>
				</tr>
			</tfoot>
		</table>		
		
		<table class="my-table">
			<tbody>
				<tr>
					<td colspan="4">
						<p>
						Please note the following:  I've put some really small print here just so that you don't really read this and appreciate that we have terms and conditions like every other business.
						</p>		
					</td>
				</tr>
			</tbody>
		</table>		
		
	</div>
	
	<div id="my-footer" zcpage-footerhtml="true" class="align-center">
		<hr />
		<br />
		<div class="zcpage-pagenumber">
			Page <span class="currentPageNumber"> </span> of <span class="totalPageNumber"> </span><br />
		</div>
		<div class="footer-copyright">
			Copyright &copy; <%=v_ThisYear%> My Company Ltd. All Rights Reserved.
		</div>
	</div> 

</div>
<%

}%>
  1.  <%{ 
  2.      /* ******************************************************************************* 
  3.      Function:       - 
  4.      Label:          Zoho Creator Page: Print_Quote 
  5.      Trigger:        This is the HTML snippet for the "Print Quote" page 
  6.      Purpose:        The below code is the HTML combined with Deluge to output a quote in this template. 
  7.      Inputs:         p_Quotes [collection of the quote form - when tick multiple records in a list] 
  8.      Outputs:        A HTML page that can be rendered in PDF or Print 
  9.   
  10.      Date Created:   2023-04-03 (Joel Lipman) 
  11.                      - Initial release 
  12.      Date Modified:    ??? 
  13.                      - ??? 
  14.   
  15.      More Information: 
  16.                      https://help.zoho.com/portal/en/kb/creator/developer-guide/others/url-patterns/articles/functionality-based-urls#To_convert_Page_to_PDF 
  17.                      https://help.zoho.com/portal/en/kb/creator/developer-guide/pages/create-and-manage-pages/articles/guidelines-for-exporting-page-into-pdf#31_PDF_Margin 
  18.   
  19.      ******************************************************************************* */ 
  20.      // 
  21.      // the publish key of the product images report 
  22.      v_ProductPhoto_PublishKey = "AAAAABBBBBCCCCDDDDEEEFFFGGGGHHHIIIJJJKKLLLMMMNNOOOPPPQQWRRRSSTTUUVVWWXXYYZZ122345567890"
  23.      // my company logo (must be a public URL to render in the PDF) 
  24.      v_LogoPhotoUrl = "https://mycompany.com/assets/img/template_logo.png"
  25.      // getting current year 
  26.      v_ThisYear = zoho.currentdate.getYear()
  27.      // converting the submitted quote record IDs as a list 
  28.      l_QuoteIDs = ifnull(input.p_Quotes,"").toList()
  29.      // 
  30.      // get customer information 
  31.      v_CurrencyHtmlEntity = "&#163;"; 
  32.      v_Firstname = ""
  33.      v_Lastname = ""
  34.      v_Email = ""
  35.      v_QuoteDate = zoho.currentdate.toString("dd-MMM-yyyy")
  36.      v_AddedBy = "The No. 1 Salesperson"
  37.      for each  v_QuoteID in l_QuoteIDs 
  38.      { 
  39.          c_Quote = Quote[ID == v_QuoteID]
  40.          if(c_Quote.count() > 0) 
  41.          { 
  42.              if(c_Quote.Name != null) 
  43.              { 
  44.                  v_Firstname = c_Quote.Name.first_name; 
  45.                  v_Lastname = c_Quote.Name.last_name; 
  46.              } 
  47.              if(c_Quote.Email != null) 
  48.              { 
  49.                  v_Email = c_Quote.Email; 
  50.              } 
  51.              v_QuoteDate = c_Quote.Modified_Time.toString("dd-MMM-yyyy")
  52.              c_AddedBy = Staff[Zoho_Creator_User == zoho.loginuser]
  53.              if(c_AddedBy.count() > 0) 
  54.              { 
  55.                  v_AddedBy = c_AddedBy.Name; 
  56.              } 
  57.              break
  58.          } 
  59.      } 
  60.      %> 
  61.  <style> 
  62.          /* page sizing */ 
  63.          @page { 
  64.              size: A4; 
  65.              margin: 0
  66.          } 
  67.   
  68.          /* CSS for when opting to print rather than render in PDF */ 
  69.          /* Overridden by zcpage-pagenumber which hides the entire div when printing */ 
  70.          .currentPageNumber { 
  71.              content: "1"
  72.          } 
  73.          .totalPageNumber { 
  74.              content: "1"
  75.          } 
  76.   
  77.          /* globals */ 
  78.          #my-header td, 
  79.          #my-container th, 
  80.          #my-container td, 
  81.          #my-footer{ 
  82.              color: #111; 
  83.              font-family: 'Lato', sans-serif !important; 
  84.              font-size: 10pt
  85.              font-weight:300
  86.          } 
  87.          .my-table{ 
  88.              width: 100%; 
  89.              margin-bottom:20px
  90.          } 
  91.          .my-table td, 
  92.          .my-table th { 
  93.              padding: 5px 10px
  94.          } 
  95.   
  96.          /* letterhead header */ 
  97.          #my-header .my-table thead{ 
  98.              margin-bottom:30px
  99.          } 
  100.          #my-header .my-table tr td:first-child{ 
  101.              width: 175px
  102.          } 
  103.          #my-header .my-table tr td:nth-child(2){ 
  104.              font-size: 7pt
  105.              text-transform: uppercase; 
  106.          } 
  107.          #my-header .my-table tr td:nth-child(3){ 
  108.              text-transform: uppercase; 
  109.              font-size: 36pt
  110.              font-weight: 100
  111.              color: #777; 
  112.              text-align: right; 
  113.              vertical-align: top; 
  114.          } 
  115.   
  116.          /* main content */ 
  117.          #my-content table:first-child td{ 
  118.              vertical-align:top; 
  119.          } 
  120.          #my-content table:first-child label{ 
  121.              text-transform: uppercase; 
  122.              color: #777; 
  123.              display:inline-block; 
  124.              width:100px
  125.          } 
  126.          #my-content table:first-child span{ 
  127.              font-weight:400
  128.              display:inline-block; 
  129.              min-width:100px
  130.          } 
  131.          #my-content table:first-child tr td:nth-child(2){ 
  132.              width: 240px
  133.          } 
  134.   
  135.          /* line items */ 
  136.          #my-line-items thead tr th{ 
  137.              color: #777; 
  138.              text-align:right; 
  139.              text-transform: uppercase; 
  140.              vertical-align: bottom; 
  141.              border-bottom: 1px solid #eee; 
  142.          } 
  143.          #my-line-items thead tr th:first-child{ 
  144.              width: 50%; 
  145.              text-align:left; 
  146.          } 
  147.          #my-line-items tbody tr td img{ 
  148.              width: 75px
  149.              height: auto; 
  150.              max-height: 75px
  151.              max-width: 75px
  152.              margin-right: 15px
  153.              float:left; 
  154.          } 
  155.          #my-line-items tbody tr td{ 
  156.              font-weight:400
  157.              text-align:right; 
  158.              vertical-align:top; 
  159.              padding: 0 10px 10px 10px
  160.          } 
  161.          #my-line-items tbody tr td span{ 
  162.              font-size: 8pt
  163.          } 
  164.          #my-line-items tbody tr td span:first-child{ 
  165.              font-size: 10pt
  166.          } 
  167.          #my-line-items tbody tr td:first-child{ 
  168.              text-align:left; 
  169.          } 
  170.          #my-line-items tbody tr td:first-child div{ 
  171.              float:left; 
  172.              display:inline-block; 
  173.          } 
  174.          #my-line-items tfoot tr th{ 
  175.              color: #777; 
  176.              text-align:right; 
  177.              text-transform: uppercase; 
  178.              vertical-align: bottom; 
  179.          } 
  180.          #my-line-items tfoot tr:nth-child(2) th{ 
  181.              color: #111; 
  182.              font-weight:400
  183.              border-top: 1px solid #eee; 
  184.          } 
  185.   
  186.          /* customer notes */ 
  187.          #my-content table:nth-child(3) p{ 
  188.              font-size:7pt
  189.          } 
  190.   
  191.          /* page footer */ 
  192.          #my-footer{ 
  193.              text-align:center; 
  194.              font-size:7pt
  195.              text-transform: uppercase; 
  196.          } 
  197.          #my-container hr{ 
  198.              border: 0
  199.              height: 1px
  200.              background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0))
  201.          } 
  202.      </style> 
  203.   
  204.  <div id="my-container" zcpage-spacing="40"> 
  205.   
  206.      <div id="my-header" zcpage-headerhtml="true"> 
  207.          <table class="my-table"> 
  208.              <tbody> 
  209.                  <tr> 
  210.                      <td><img src="<%=v_LogoPhotoUrl%>" width="150" height="auto" alt="My Company" /></td> 
  211.                      <td>My Company Ltd<br />Test Street,<br />Test County TEST1 ZIP1<br /><br />+44 (0)1234 567890<br  />This email address is being protected from spambots. You need JavaScript enabled to view it.<br  />www.mycompany.com<br  /> 
  212.                      <td>QUOTE</td> 
  213.                  </tr> 
  214.              </tbody> 
  215.          </table> 
  216.          <hr /> 
  217.          <br /> 
  218.      </div> 
  219.   
  220.      <div id="my-content"> 
  221.          <table class="my-table"> 
  222.              <thead> 
  223.                  <tr> 
  224.                      <td> 
  225.                          <label>Quote For:</label><br /> 
  226.                          <span><%=v_Firstname%> <%=v_Lastname%></span><br /> 
  227.                          <span><%=v_Email%></span> 
  228.                      </td> 
  229.                      <td> 
  230.                          <label>Quote Date:</label> <span><%=v_QuoteDate%></span><br /> 
  231.                          <label>Quote By:</label> <span><%=v_AddedBy%></span><br /> 
  232.                      </td> 
  233.                  </tr> 
  234.              </thead> 
  235.          </table> 
  236.   
  237.          <table class="my-table" id="my-line-items"> 
  238.              <thead> 
  239.                  <tr> 
  240.                      <th>Item Description</th> 
  241.                      <th>Quantity</th> 
  242.                      <th>Unit Price</th> 
  243.                      <th>Unit Discount</th> 
  244.                  </tr> 
  245.              </thead> 
  246.              <tbody> 
  247.  <% 
  248.      v_TotalQuantity = 0
  249.      v_TotalPrice = 0.00
  250.      v_TotalDiscount = 0.00
  251.      v_TotalAmount = 0.00
  252.      for each  v_QuoteID in l_QuoteIDs 
  253.      { 
  254.          c_Quote = Quote[ID == v_QuoteID]
  255.          if(c_Quote.count() > 0) 
  256.          { 
  257.              v_LineItemTotal = 0.00
  258.              v_ThisQuantity = ifnull(c_Quote.Quantity,1).toLong()
  259.              v_TotalQuantity = v_TotalQuantity + v_ThisQuantity; 
  260.              // 
  261.              v_PriceDisplay = if(c_Quote.Unit_Price && c_Quote.Unit_Price > 0,v_CurrencyHtmlEntity + c_Quote.Unit_Price.round(2),"-")
  262.              v_PriceValue = if(c_Quote.Unit_Price && c_Quote.Unit_Price > 0,c_Quote.Unit_Price,0)
  263.              v_PriceTotal = v_PriceValue * v_ThisQuantity; 
  264.              v_TotalPrice = (v_TotalPrice + v_PriceTotal).round(2)
  265.              // 
  266.              v_DiscountDisplay = if(c_Quote.Discount_Amount && c_Quote.Discount_Amount > 0,v_CurrencyHtmlEntity + c_Quote.Discount_Amount.round(2),"-")
  267.              v_DiscountValue = if(c_Quote.Discount_Amount && c_Quote.Discount_Amount > 0,c_Quote.Discount_Amount,0)
  268.              v_DiscountTotal = v_DiscountValue * v_ThisQuantity; 
  269.              v_TotalDiscount = (v_TotalDiscount + v_DiscountTotal).round(2)
  270.              // 
  271.              v_LineItemTotal = (v_PriceTotal - v_DiscountTotal).round(2)
  272.              v_TotalAmount = (v_TotalAmount + v_LineItemTotal).round(2)
  273.              // 
  274.              // 
  275.              // product photo 
  276.              c_Product = Products[ID == c_Quote.Product]
  277.              if(c_Product.count() > 0) 
  278.              { 
  279.                  v_ProductPhotoFile = c_Model.Product.getSuffix("http").getPrefix("\"").getSuffix("/image/")
  280.                  v_ProductPhotoURL = "https://creatorexport.zoho.com/file" + zoho.appuri + "Products/" + c_Product.ID + "/Photo/image-download/" + v_ProductPhoto_PublishKey + "?filepath=" + v_ProductPhotoFile; 
  281.              } 
  282.              v_ItemImgDisp = "<img src='" + v_ProductPhotoURL + "' alt='" + c_Quote.Product.Product_Name + "' />"
  283.              // 
  284.              // item and description 
  285.              v_ItemDescDisp = "<span>" + c_Quote.Product.Product_Name + "</span><br />"
  286.              if(c_Quote.Special_Edition.containsIgnoreCase("yes")) 
  287.              { 
  288.                  v_ItemDescDisp = "<span>SPECIAL EDITION</span><br />"
  289.              } 
  290.              // 
  291.              // other properties to add to the description 
  292.              v_ItemDescDisp = v_ItemDescDisp + "<span>Awesome Reason To Buy This #1</span><br />"; 
  293.              v_ItemDescDisp = v_ItemDescDisp + "<span>Awesome Reason To Buy This #2</span><br />"; 
  294.              // 
  295.              // condition grade 
  296.              v_ConditionChar = ifnull(c_Quote.Grade_Condition,"C").subString(0,1)
  297.              v_ConditionDisp = if(v_ConditionChar == "N","New",v_ConditionChar + " Grade")
  298.              v_ItemDescDisp = v_ItemDescDisp + "<span>" + v_ConditionDisp + "</span><br />"
  299.              // 
  300.              // unique item reference 
  301.              if(c_Quote.Quote_Reference.contains("Q-")) 
  302.              { 
  303.                  v_ItemDescDisp = v_ItemDescDisp + "<span><i>Ref. " + c_Quote.Quote_Reference + "</i></span><br />"
  304.              } 
  305.              %> 
  306.                  <tr> 
  307.                      <td><%=v_ItemImgDisp%><div><%=v_ItemDescDisp%></div></td> 
  308.                      <td><%=v_ThisQuantity%></td> 
  309.                      <td><%=v_PriceDisplay%></td> 
  310.                      <td><%=v_DiscountDisplay%></td> 
  311.                      <td><%=v_CurrencyHtmlEntity><%=v_LineItemTotal%></td> 
  312.                  </tr> 
  313.  <% 
  314.          } 
  315.      } 
  316.      %> 
  317.  </tbody> 
  318.              <tfoot> 
  319.                  <tr> 
  320.                      <th></th> 
  321.                      <th>Total Quantity</th> 
  322.                      <th>Total Price</th> 
  323.                      <th>Total Discount</th> 
  324.                      <th>Total Amount</th> 
  325.                  </tr> 
  326.                  <tr> 
  327.                      <th></th> 
  328.                      <th><%=v_TotalQuantity%></th> 
  329.                      <th><%=v_CurrencyHtmlEntity><%=v_TotalPrice%></th> 
  330.                      <th><%=v_CurrencyHtmlEntity><%=v_TotalDiscount%></th> 
  331.                      <th><%=v_CurrencyHtmlEntity><%=v_TotalAmount%></th> 
  332.                  </tr> 
  333.              </tfoot> 
  334.          </table> 
  335.   
  336.          <table class="my-table"> 
  337.              <tbody> 
  338.                  <tr> 
  339.                      <td colspan="4"> 
  340.                          <p> 
  341.                          Please note the following:  I've put some really small print here just so that you don't really read this and appreciate that we have terms and conditions like every other business. 
  342.                          </p> 
  343.                      </td> 
  344.                  </tr> 
  345.              </tbody> 
  346.          </table> 
  347.   
  348.      </div> 
  349.   
  350.      <div id="my-footer" zcpage-footerhtml="true" class="align-center"> 
  351.          <hr /> 
  352.          <br /> 
  353.          <div class="zcpage-pagenumber"> 
  354.              Page <span class="currentPageNumber"> </span> of <span class="totalPageNumber"> </span><br /> 
  355.          </div> 
  356.          <div class="footer-copyright"> 
  357.              Copyright &copy; <%=v_ThisYear%> My Company Ltd. All Rights Reserved. 
  358.          </div> 
  359.      </div> 
  360.   
  361.  </div> 
  362.  <% 
  363.   
  364.  }%> 

Additional Notes to Self:
  • Some customers will want a column for VAT/Tax (Display amount and then percentage beneath in smaller)
  • Line Item Totals have been added to the above code retrospectively without proper testing.
  • Aggregate of total amount may be unnecessary as this could be the same under price total.
  • Where a price is applied, values should be retained in decimal format with currency symbol prefixed if value is greater than zero.

Source(s):

Category: Zoho :: Article: 873

Credit where Credit is Due:


Feel free to copy, redistribute and share this information. All that we ask is that you attribute credit and possibly even a link back to this website as it really helps in our search engine rankings.

Disclaimer: Please note that the information provided on this website is intended for informational purposes only and does not represent a warranty. The opinions expressed are those of the author only. We recommend testing any solutions in a development environment before implementing them in production. The articles are based on our good faith efforts and were current at the time of writing, reflecting our practical experience in a commercial setting.

Thank you for visiting and, as always, we hope this website was of some use to you!

Kind Regards,

Joel Lipman
www.joellipman.com

Related Articles

Joes Revolver Map

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

Ethereum:
Donate to Joel Lipman with Ethereum 0xb038962F3809b425D661EF5D22294Cf45E02FebF
© 2024 Joel Lipman .com. All Rights Reserved.