HTML Invoice to PDF in JavaScript: JSPDF Example

by Vincy. Last modified on August 4th, 2021.

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 of 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 tying with any subscription plans.

This example provides a custom solution of creating a simple invoice PDF in JavaScript. Creating PDF in JavaScript will be simple compare to the server-side PDF generation libraries.

Generate invoice PDF – Best practices

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.

  • Use a professional template to impress customers.
  • Make sure of adding all-important details for an invoice. For example,
    • a unique identification number.
    • the generated and due date.
    • purchased items.
    • customer details
  • Clear split up for the invoice amount.
  • Specify tax and discount column. Specify the mode of tax/discount whether is flat or a percentage.
  • Separate billing and delivery addresses if they are different.

There are more things to keep in mind for having a full-fledged invoicing system for our business.

  • Be transparent about the conditions applied.
  • Specify the mode of payment, pending dues if any.
  • Practice a proper reminder sending mechanism before the due date.

HTML invoice to PDF – Example

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 convert HTML to PDF. 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.

Files structure

html invoice pdf example files

Database script


--
-- 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;

Purchase order list with generate invoice option

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>

Preview HTML invoice showing ordered items

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 invoice into PDF. We have seen an example code to convert a static HTML to PDF in JavaScript using jsPDF.

This template loads jsPDF library and the dependent htmltocanvas library files. The PDF conversion process on the client-side needs this reference. This is to 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>

PHP backend code to access database for purchase order invoice

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;
    }
}

Convert HTML invoice to PDF in JavaScript using jsPDF

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
 });
}

Output – HTML invoice PDF

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.

order with generate invoice link

The HTML invoice includes the following details to be converted PDF in JavaScript.

  • Purchase order list
  • Customer details
  • Billing and shipping addresses

The below screenshot shows the output of the invoice PDF template. This example will show this preview before downloading the invoice PDF.

Generated Invoice PDF in JavaScript

Conclusion

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

Leave a Reply

Your email address will not be published. Required fields are marked *

↑ Back to Top