The template engine for PDF Invoices & Packing Slips is built for flexibility, and includes several action hooks where you can output custom content:
Hook | Position | Arguments |
---|---|---|
wpo_wcpdf_before_document | Before all content on the document | $document_type, $order |
wpo_wcpdf_after_document | After all content on the document (after the footer) | $ |
wpo_wcpdf_before_shop_name | Before the shop name | $ |
wpo_wcpdf_after_shop_name | After the shop name | $ |
wpo_wcpdf_before_shop_address | Before the shop address | $ |
wpo_wcpdf_after_shop_address | After the shop address | $ |
wpo_wcpdf_before_document_label | Before the document label (Invoice, Packing Slip, etc.) | $ |
wpo_wcpdf_after_document_label | After the document label (Invoice, Packing Slip, etc.) | $ |
wpo_wcpdf_before_billing_address | Before the billing address | $ |
wpo_wcpdf_after_billing_address | After the billing address | $ |
wpo_wcpdf_before_shipping_address | Before the shipping address | $ |
wpo_wcpdf_after_shipping_address | After the shipping address | $ |
wpo_wcpdf_before_order_data | Before the order data (invoice number, order date, etc.) note that this is inside a table, and you should output the data as an html table row/cells | $ |
wpo_wcpdf_after_order_data | After the order data note that this is inside a table, and you should output the data as an html table row/cells | $ |
wpo_wcpdf_before_order_details | Before the order details table with all items | $ |
wpo_wcpdf_after_order_details | After the order details table | $ |
wpo_wcpdf_before_item_meta | Before the item meta (for each item in the order details table) | $ |
wpo_wcpdf_after_item_meta | After the item meta (for each item in the order details table) | $ |
wpo_wcpdf_before_document_notes | Before the document notes (left of the order totals). Note that this is only available on the invoice. | $ |
wpo_wcpdf_after_document_notes | After the document notes (left of the order totals). Note that this is only available on the invoice. | $ |
wpo_wcpdf_before_customer_notes | Before the customer/shipping notes (left of the order totals) | $ |
wpo_wcpdf_after_customer_notes | After the customer/shipping notes (left of the order totals) | $ |
wpo_wcpdf_before_footer | Before the footer | $ |
wpo_wcpdf_after_footer | After the footer | $ |
Using the template action hooks #
If you have never used WordPress action hooks before, this may sound like abracadabra. Luckily it’s not that difficult, although some basic PHP knowledge does help a lot if you’re doing this.
There are two places where you can put the code from the examples below. You can either insert the code into your theme functions / functions.php (read this if you have never edited that file before!) or if you have already created a custom PDF template, you can put it in the template-functions.php file inside your custom template folder.
Example 1: Print a delivery date on the packing slip #
This will print a custom field (for example, delivery_date
) below the regular order data on the packing slip. If you don’t know the name of your custom field, follow this guide: Finding WooCommerce Custom Fields
add_action( 'wpo_wcpdf_after_order_data', 'wpo_wcpdf_delivery_date', 10, 2 ); function wpo_wcpdf_delivery_date ($document_type, $order) { $document = wcpdf_get_document( $document_type, $order ); if ($document_type == 'packing-slip') { ?> <tr class="delivery-date"> <th>Delivery Date:</th> <td><?php $document->custom_field('delivery_date'); ?></td> </tr> <?php } }
Some plugins save the delivery date as a UNIX timestamp, which is a big number, like 1483228800. In this case, you will need to convert this to human readable format first.
add_action( 'wpo_wcpdf_after_order_data', 'wpo_wcpdf_delivery_date', 10, 2 ); function wpo_wcpdf_delivery_date ($document_type, $order) { if ($document_type == 'packing-slip') { // get the delivery date from the order $delivery_date = $order->get_meta('delivery_date'); // convert the delivery date to a human readable format (using the WooCommerce/WordPress date format settings) $formatted_delivery_date = date_i18n( wc_date_format(), $delivery_date ); ?> <tr class="delivery-date"> <th>Delivery Date:</th> <td><?php echo $formatted_delivery_date; ?></td> </tr> <?php } }
Example 2: Print a custom text after the order details table #
This will print a simple text below the order details table on all PDF documents.
add_action( 'wpo_wcpdf_after_order_details', 'wpo_wcpdf_custom_text', 10, 2 ); function wpo_wcpdf_custom_text ($document_type, $order) { ?> <div class="custom-text"> If you have any questions about your order, don't hesitate to contact us! </div> <?php }
Or restricted to the PDF invoice and BACS payment method:
add_action( 'wpo_wcpdf_after_order_details', 'wpo_wcpdf_custom_text', 10, 2 ); function wpo_wcpdf_custom_text ($document_type, $order) { $payment_method = $order->get_payment_method(); if ($document_type == 'invoice' && $payment_method == 'bacs') { ?> <div class="custom-text"> Thank you for your order. Please send the full amount to our bank account: 123-456-789. After we have received your payment, you will receive a notification and we will send you the goods. </div> <?php } }
Or a text about intra-community supply, restricted to invoices (& proforma invoices & credit notes) without tax:
add_action( 'wpo_wcpdf_after_order_details', 'wpo_wcpdf_tax_exempt', 10, 2 ); function wpo_wcpdf_tax_exempt( $document_type, $order ) { // only in financial documents if ( ! in_array( $document_type, array( 'invoice', 'proforma', 'credit-note' ) ) ) { return; } // get shop location $shop_base_location = wc_get_base_location(); // refund orders don't store all tax data that the parent does $tax_order = $order->get_type() == 'shop_order_refund' ? wc_get_order( $order->get_parent_id() ) : clone $order; // get billing country $billing_country = $tax_order->get_billing_country(); // check if any tax was charged and if billing country is outside of shop base country if ( $tax_order->get_total_tax() == 0 && $tax_order->get_total() > 0 && $billing_country != $shop_base_location['country'] ) { ?> <div class="tax-exempt"> Tax free under intra-community supply. </div> <?php } }
Example 3: Print a list of categories under each product name #
This example prints a comma separated list of categories for each product under the product name (after the item meta).
add_action( 'wpo_wcpdf_after_item_meta', 'wpo_wcpdf_show_product_categories', 10, 3 ); function wpo_wcpdf_show_product_categories ( $document_type, $item, $order ) { // get a comma separated list of categories (category links stripped) if (isset($item['product'])) { echo '<div class="product-categories">Categories: '.strip_tags( wc_get_product_category_list( $item['product']->get_id() ) ).'</div>'; } }
Example 4: Print a due date on the invoice #
Calculate a due date 14 days after the invoice date (see the + 14 days
bit and strtotime() for reference)
add_action( 'wpo_wcpdf_after_order_data', 'wpo_wcpdf_due_date', 10, 2 ); function wpo_wcpdf_due_date ($document_type, $order) { if ($document_type == 'invoice') { // put due date only on invoice $invoice = wcpdf_get_invoice( $order ); if ( $invoice_date = $invoice->get_date() ) { $due_date = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date->date_i18n('Y-m-d H:i:s') . ' + 14 days') ); ?> <tr class="due-date"> <th>Due Date:</th> <td><?php echo $due_date; ?></td> </tr> <?php } } }
Example 5: Printing payment instructions on the Proforma Invoice #
You may want to add some custom styles to make this look a bit better.
add_action( 'wpo_wcpdf_after_order_details', 'wpo_wcpdf_payment_instructions', 10, 2 ); function wpo_wcpdf_payment_instructions ($document_type, $order) { if ($document_type == 'proforma') { // change 'proforma' to 'invoice' if you want to put this on the invoice WC()->payment_gateways(); $order_id = $order->get_id(); $payment_method = $order->get_payment_method(); do_action( 'woocommerce_thankyou_' . $payment_method, $order_id ); } }