Invoicing is a recommended legal process to be raised by a seller to the buyer. Enabling an invoice generation feature in an eCommerce website is one the important works.
There are numerous tools online to generate invoices. Most of them are paid products on a subscription basis.
Sometimes the need for invoice generation may be minimal. In such cases, I prefer to go with a custom solution instead of trying with any subscription plans.
This example provides a custom solution for creating a simple invoice PDF in JavaScript. Creating PDF in JavaScript will be simple compared to the server-side PDF generation libraries.
Invoicing is not something only to generate and issue to the buyers. It’s a complex business module to keep tracking of the buyers paying late. So, we should follow the world’s best practices in creating invoices.
There are more things to keep in mind for having a full-fledged invoicing system for our business.
This example gives a solution to generate invoice PDF for the purchased items. It converts HTML invoices into PDF in JavaScript.
It uses the jsPDF JavaScript library to generate PDF from HTML. We have seen already how to integrate jsPDF into an application.
The source HTML invoice is loaded with dynamic data. It displays ordered items with customer and shipping/billing address details.
The following file structure and the database are created for this example. The file structure will help you to understand the example code.
This example database will have the order and order-item tables. These tables map the customer and purchased product details with appropriate key definitions.
--
-- Table structure for table `tbl_customer`
--
CREATE TABLE `tbl_customer` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`phone` varchar(22) DEFAULT NULL,
`billing_address1` varchar(150) NOT NULL,
`billing_address2` varchar(150) NOT NULL,
`billing_city` char(30) NOT NULL,
`billing_state` char(30) NOT NULL,
`billing_zip` varchar(15) NOT NULL,
`billing_country` char(50) NOT NULL,
`shipping_address1` varchar(150) NOT NULL,
`shipping_address2` varchar(150) NOT NULL,
`shipping_city` char(30) NOT NULL,
`shipping_state` char(30) NOT NULL,
`shipping_zip` varchar(15) NOT NULL,
`shipping_country` char(50) NOT NULL,
`create_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_customer`
--
INSERT INTO `tbl_customer` (`id`, `name`, `email`, `phone`, `billing_address1`, `billing_address2`, `billing_city`, `billing_state`, `billing_zip`, `billing_country`, `shipping_address1`, `shipping_address2`, `shipping_city`, `shipping_state`, `shipping_zip`, `shipping_country`, `create_at`) VALUES
(1, 'John', 'john@gmail.com', '7468937985634', 'Golden Ridge Road', 'Kelly Drive', 'Boise', 'Idaho', '83716', 'United States', 'Golden Ridge Road', 'Kelly Drive', 'Boise', 'Idaho', '83716', 'United States', '2021-06-02 14:23:27'),
(2, 'David', 'william@gmail.com', '4896378638555', 'Snyder Avenue', 'West Virginia', 'Bluefield', 'Iowa', '48075', 'United States', 'Snyder Avenue', 'West Virginia', 'Bluefield', 'Iowa', '48075', 'United States', '2021-06-02 14:26:02');
-- --------------------------------------------------------
--
-- Table structure for table `tbl_order`
--
CREATE TABLE `tbl_order` (
`id` int(11) NOT NULL,
`order_id` varchar(255) NOT NULL,
`invoice_number` varchar(255) NOT NULL,
`customer_id` int(11) NOT NULL,
`customer_email` varchar(255) NOT NULL,
`amount` decimal(10,2) NOT NULL,
`shipping_amount` decimal(10,2) NOT NULL,
`currency` varchar(255) NOT NULL,
`payment_type` varchar(255) NOT NULL,
`payment_status` varchar(255) NOT NULL,
`order_status` varchar(255) NOT NULL,
`order_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_order`
--
INSERT INTO `tbl_order` (`id`, `order_id`, `invoice_number`, `customer_id`, `customer_email`, `amount`, `shipping_amount`, `currency`, `payment_type`, `payment_status`, `order_status`, `order_at`) VALUES
(1, '1iRxJtOtAlai4v6LEuGI', '786567', 1, 'john@gmail.com', '7050.00', '0.00', 'USD', 'Paypal', 'Completed', 'Delivered', '2021-06-02 14:19:02'),
(2, '1iRxJtOtAlai4v6LEuGR', '486231', 2, 'william@gmail.com', '6500.00', '50.00', 'USD', 'Paypal', 'Completed', 'Delivered', '2021-06-02 14:19:14');
-- --------------------------------------------------------
--
-- Table structure for table `tbl_order_item`
--
CREATE TABLE `tbl_order_item` (
`id` int(11) NOT NULL,
`order_id` varchar(255) NOT NULL,
`product_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`quantity` int(11) NOT NULL,
`item_price` decimal(10,2) NOT NULL,
`create_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_order_item`
--
INSERT INTO `tbl_order_item` (`id`, `order_id`, `product_id`, `name`, `quantity`, `item_price`, `create_at`) VALUES
(1, '1iRxJtOtAlai4v6LEuGI', 1, 'FinePix Pro2 3D Camera', 1, '6500.00', '2021-06-02 09:42:40'),
(2, '1iRxJtOtAlai4v6LEuGI', 2, 'EXP Portable Hard Drive', 1, '550.00', '2021-06-02 09:46:31'),
(3, '1iRxJtOtAlai4v6LEuGR', 3, 'Luxury Ultra thin Wrist Watch', 1, '6500.00', '2021-06-02 14:10:35');
-- --------------------------------------------------------
--
-- Table structure for table `tbl_product`
--
CREATE TABLE `tbl_product` (
`id` int(11) NOT NULL,
`product_title` varchar(255) NOT NULL,
`description` text NOT NULL,
`price` decimal(10,2) NOT NULL,
`create_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_product`
--
INSERT INTO `tbl_product` (`id`, `product_title`, `description`, `price`, `create_at`) VALUES
(1, 'FinePix Pro2 3D Camera', 'FinePix Pro2 3D Camera', '6500.00', '2021-06-02 14:26:40'),
(2, 'EXP Portable Hard Drive', 'EXP Portable Hard Drive', '550.00', '2021-06-02 14:26:48'),
(3, 'Luxury Ultra thin Wrist Watch', 'Luxury Ultra thin Wrist Watch', '6500.00', '2021-06-02 14:26:54');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tbl_customer`
--
ALTER TABLE `tbl_customer`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `tbl_order`
--
ALTER TABLE `tbl_order`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `tbl_order_item`
--
ALTER TABLE `tbl_order_item`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `tbl_product`
--
ALTER TABLE `tbl_product`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tbl_customer`
--
ALTER TABLE `tbl_customer`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT for table `tbl_order`
--
ALTER TABLE `tbl_order`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT for table `tbl_order_item`
--
ALTER TABLE `tbl_order_item`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT for table `tbl_product`
--
ALTER TABLE `tbl_product`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
This is the landing page to display the purchase order list in a tabular form. It displays all the orders with the reference id, ordered date, total amount and more details.
On each row, it has an HTML link to trigger generating invoices. It is to redirect the user to view a preview before downloading the invoice.
The purchase orders are from the database. The order table columns are properly added with the key mappings. It relates to the ordered products and also the customer who places the order.
This list of items shows the data from the order master table. On clicking the “Generate Invoice” link, the user can see the list of products purchased in this order.
index.php
<?php
require_once __DIR__ . '/Model/Order.php';
$orderModel = new Order();
$result = $orderModel->getAllOrder();
?>
<HTML>
<HEAD>
<TITLE>Convert Invoice HTML to Pdf</TITLE>
<link href="assets/css/style.css" type="text/css" rel="stylesheet" />
</HEAD>
<BODY>
<?php
if (! empty($result)) {
?>
<div class="container">
<h2 class="text-center">Purchase Orders</h2>
<table class="order-list">
<thead>
<tr>
<th class="text-left">Order Reference ID</th>
<th class="text-left">Date</th>
<th class="text-left">Customer</th>
<th class="text-right">Amount</th>
<th class="text-left">Status</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
foreach ($result as $k => $v) {
$customerResult = $orderModel->getCustomer($result[$k]["customer_id"]);
?>
<tr>
<td><?php echo $result[$k]["order_id"];?></td>
<td class="text-nowrap"><?php echo date('d-m-Y', strtotime($result[$k]["order_at"]));?></td>
<td><?php echo $customerResult[0]["name"];?></td>
<td class="text-right"><?php echo $result[$k]["amount"];?></td>
<td class="text-left"><?php echo $result[$k]["payment_status"];?></td>
<td class="text-right"><a target="_blank"
title="Generate Invoice" class="action"
href="Template/invoice-template.php/?order_id=<?php echo $result[$k]["order_id"];?>">
Generate Invoice </a></td>
</tr>
<?php }?>
</tbody>
</table>
</div>
<?php
}
?>
</BODY>
</HTML>
This is a HTML template created to format the invoice PDF in JavaScript. This template is the source to be supplied to the JavaScript PDF generation library.
I used jsPDF library to convert HTML invoices into PDF. We have seen an example code to convert a static HTML to PDF in JavaScript using jsPDF.
This template loads the jsPDF library and the dependent htmltocanvas library files. The PDF conversion process on the client-side needs this reference. This is to import and instantiate the library classes.
This template file includes PHP scripts to fetch purchase orders from the database. It reads order, items and customer to load the data into the template.
The PHP classes created for this example prepare queries and handle database operations.
Template/invoice-template.php
<?php
require_once __DIR__ . '/../Model/Order.php';
$orderModel = new Order();
$orderResult = $orderModel->getOrder($_GET["order_id"]);
$orderItemResult = $orderModel->getOrderItems($orderResult[0]["order_id"]);
$customerResult = $orderModel->getCustomer($orderResult[0]["customer_id"]);
?>
<html>
<head>
<title>Invoice number - <?php echo $orderResult[0]["order_id"];?></title>
<link href="../../assets/css/style.css" type="text/css" rel="stylesheet" />
<!-- Template loads the jsPDF and htmlcanvas library js -->
<script src="../../node_modules/jspdf/dist/jspdf.umd.min.js"></script>
<script type="text/javascript"
src="../../node_modules/html2canvas/dist/html2canvas.js"></script>
</head>
<body>
<div class="container" id="html-template">
<<!-- invokes function to convert HTML invoice to PDF in JavaScript-->
<div class="download-link text-right">
<button class="btn-convert" onclick="convertHTMLToPDF()">Download</button>
</div>
<div class="text-right">
<b>Sender:</b> Vincy
</div>
<div class="border-top text-left">
<div class="heading">Invoice</div>
</div>
<table class="invoice-tbl">
<tr>
<td><b>Invoice number:</b> # <?php echo $orderResult[0]["order_id"];?></td>
</tr>
<tr>
<td><b>Date:</b> <?php echo date('d-m-Y', strtotime($orderResult[0]["order_at"]));?></td>
</tr>
</table>
<div></div>
<div class="border-bottom">
<table class="items-tbl">
<tr>
<th>Product</th>
<th class="text-right">Price ($)</th>
<th class="text-right">Quantity</th>
<th class="text-right">Total ($)</th>
</tr>
<?php
$total = 0;
foreach ($orderItemResult as $k => $v) {
$price = $orderItemResult[$k]["item_price"] * $orderItemResult[$k]["quantity"];
$total += $price;
?>
<tr>
<td><?php echo $orderItemResult[$k]["name"];?></td>
<td class="text-right"><?php echo number_format($orderItemResult[$k]["item_price"], 2);?></td>
<td class="text-right"><?php echo $orderItemResult[$k]["quantity"];?></td>
<td class="text-right"><?php echo number_format($price, 2);?></td>
</tr>
<?php
}
?>
<?php
$grandTotal = $total;
if (! empty($orderResult[0]["shipping_amount"]) && $orderResult[0]["shipping_amount"] != "0.00") {
$grandTotal = $total + $orderResult[0]["shipping_amount"];
?>
<tr class="text-bold">
<td class="text-right" colspan="3">Shipping ($)</td>
<td class="text-right">
<?php echo number_format($orderResult[0]["shipping_amount"], 2);?></td>
</tr>
<?php
}
?>
<tr class="text-bold">
<td class="text-right" colspan="3">Grand Total ($)</td>
<td class="text-right">
<?php echo number_format($grandTotal, 2);?></td>
</tr>
</table>
</div>
<h3>Customer details:</h3>
<div>
<div class="cus-label" style="color: #000; padding: 5px;">
Name: <span class="cus-field">
<?php echo $customerResult[0]["name"];?></span>
</div>
<div class="cus-label">
Email: <span class="cus-field">
<?php echo $customerResult[0]["email"];?></span>
</div>
<?php
if (! empty($customerResult[0]["phone"])) {
?>
<div class="cus-label">
Phone Number: <span class="cus-field">
<?php echo $customerResult[0]["phone"];?></span>
</div>
<?php
}
?>
</div>
<h3>Billing Address:</h3>
<div>
<?php
if (! empty($customerResult[0]["billing_address1"])) {
?>
<div class="cus-label">
Address1: <span class="cus-field">
<?php echo $customerResult[0]["billing_address1"];?></span>
</div>
<?php
}
if (! empty($customerResult[0]["billing_address2"])) {
?>
<div class="cus-label">
Address2: <span class="cus-field">
<?php echo $customerResult[0]["billing_address2"];?></span>
</div>
<?php
}
if (! empty($customerResult[0]["billing_city"])) {
?>
<div class="cus-label">
City: <span class="cus-field">
<?php echo $customerResult[0]["billing_city"];?></span>
</div>
<?php
}
if (! empty($customerResult[0]["billing_state"])) {
?>
<div class="cus-label">
State: <span class="cus-field">
<?php echo $customerResult[0]["billing_state"];?></span>
</div>
<?php } if (! empty($customerResult[0]["billing_country"])){ ?>
<div class="cus-label">
Country: <span class="cus-field">
<?php $customerResult[0]["billing_country"];?></span>
</div>
<?php } if (! empty($customerResult[0]["billing_zip"])) {?>
<div class="cus-label">
Zipcode: <span class="cus-field">
<?php echo $customerResult[0]["billing_zip"];?></span>
</div>
<?php }?>
</div>
<h3>Shipping Address:</h3>
<div>
<?php if (! empty($customerResult[0]["shipping_address1"])) {?>
<div class="cus-label">
Address1: <span class="cus-field">
<?php echo $customerResult[0]["shipping_address1"];?></span>
</div>
<?php } if (! empty($customerResult[0]["shipping_address2"])) {?>
<div class="cus-label">
Address2: <span class="cus-field">
<?php echo $customerResult[0]["shipping_address2"];?></span>
</div>
<?php } if (! empty($customerResult[0]["shipping_city"])) {?>
<div class="cus-label">
City: <span class="cus-field">
<?php echo $customerResult[0]["shipping_city"];?></span>
</div>
<?php
}
if (! empty($customerResult[0]["shipping_state"])) {
?>
<div class="cus-label">
State: <span class="cus-field">
<?php echo $customerResult[0]["shipping_state"];?></span>
</div>
<?php
}
if (! empty($customerResult[0]["shipping_country"])) {
?>
<div class="cus-label">
Country: <span class="cus-field">
<?php echo $customerResult[0]["shipping_country"]; ?></span>
</div>
<?php
}
if (! empty($customerResult[0]["shipping_zip"])) {
?>
<div class="cus-label">
Zipcode: <span class="cus-field">
<?php echo $customerResult[0]["shipping_zip"]; ?></span>
</div>
<?php
}
?> </div>
</div>
<script src="../../assets/js/convert.js"></script>
</body>
</html>
These two files deal with pure PHP code to read data to load into the invoice template.
The getAllOrder() function reads data from tbl_order and returns an array. The getOrder() function receives order_id and fetches purchased items of a particular order.
The landing page uses the first function to display the list of orders. On clicking the “Generate Invoice” linkit shows an invoice preview page. On that page, it invokes the getOrder() function to load the dynamic billing data.
The order master table maps the customer details with the customer_id column.
This PHP class includes functions to retrieve orders, purchased products and customer details. Those are loaded dynamically into the HTML invoice template.
Model/Order.php
<?php
use Phppot\DataSource;
class Order
{
private $ds;
function __construct()
{
require_once __DIR__ . './../lib/DataSource.php';
$this->ds = new DataSource();
}
function getAllOrder()
{
$sql = "SELECT * FROM tbl_order";
$result = $this->ds->select($sql);
return $result;
}
function getOrder($orderId)
{
$query = "SELECT * FROM tbl_order WHERE order_id = ?";
$paramType = 's';
$paramValue = array(
$orderId
);
$result = $this->ds->select($query, $paramType, $paramValue);
return $result;
}
function getCustomer($customerId)
{
$query = "SELECT * FROM tbl_customer WHERE id = ?";
$paramType = 's';
$paramValue = array(
$customerId
);
$result = $this->ds->select($query, $paramType, $paramValue);
return $result;
}
function getOrderItems($orderId)
{
$query = "SELECT * FROM tbl_order_item WHERE order_id = ?";
$paramType = 's';
$paramValue = array(
$orderId
);
$result = $this->ds->select($query, $paramType, $paramValue);
return $result;
}
}
We have seen the jsPDF script to convert HTML into PDF in JavaScript. This function imports the jsPDF library and instantiates it.
It sets the PDF settings at the time of instantiation. There are various options to set explicitly while calling the jsPDF classes. This example sets only the paper size, dimension and orientation.
It uses the HTML invoice container reference to call the jsPDF .html() function.
This function defines the source HTML, and the axis coordinates to create the PDF. It also defines a callback method to prompt the user to save the PDF document.
assets/js/convert.js
function convertHTMLToPDF() {
const { jsPDF } = window.jspdf;
var doc = new jsPDF('1', 'mm', [1200, 1210]);
var pdfjs = document.querySelector('#html-template');
// Convert HTML invoice template source into PDF in JavaScript
doc.html(pdfjs, {
callback: function(doc) {
doc.save("output.pdf");
},
x: 10,
y: 10
});
}
This example lists the purchase order on a landing page. The below screenshot shows this list. It provides a link to generate invoices for the ordered items.
The HTML invoice includes the following details to be converted PDF in JavaScript.
The below screenshot shows the output of the invoice PDF template. This example will show this preview before downloading the invoice PDF.
So, we learn how to convert a HTML invoice into PDF in JavaScript. We have seen examples to use PHP to generate invoices with dynamic data.
This may help you to understand generating invoices in a PDF format. You can use this code to add the invoice generation feature to your application.
Download
This is a very helpful example. Loved how you took us through the whole process!
Thank you Andrew.
Hi Vicky, thank you very much for this code. Awesome!! Used it and works streight out of the box!!. Question …. I tested it out on a mobile device and the invoice is not A4 size but size of the mobile phone screen. Is there any way to fix this?
Hi Julius, I didn’t check it in the mobile view port. I will soon check it out and update the article.