Pdf Files

One of the interaction with the Customers involve sending mails, including pdf files.

For example, for every order you do on a Shop as a Customer we expect to receive an Invoice.

Working on what make a shop an unique experience, as a Developer you will be asked to customize every PDF files that PrestaShop can produce.

You can alter the rendering using the provided data or push the PDF Rendering to its best, introducing your very own information that will made your PDF files amazing.

Customize the PDF files templates

All PDF generated by PrestaShop are using Smarty templates and TCPDF as PDF rendering engine.

You will find all theses templates in the pdf folder, for each use case managed natively by the Core.

You can alter the Core files OR override them by copying them on themes/<your-theme>/pdf folder.

It’s not possible to override a PDF template in a module.

Delivery Slip

Delivery Slips are available in Orders > Delivery Slips section of the Back Office.

The following files are used to generate the PDF file:

pdf
├── delivery-slip.addresses-tab.tpl
├── delivery-slip.payment-tab.tpl
├── delivery-slip.product-tab.tpl
├── delivery-slip.style-tab.tpl
├── delivery-slip.summary-tab.tpl
└── delivery-slip.tpl

Invoices

Invoices are available in Orders > Invoices section of the Back Office and in Orders List section of the Customer Back Office.

The following files are used to generate the PDF file:

pdf
├── invoice.addresses-tab.tpl
├── invoice-b2b.tpl
├── invoice.note-tab.tpl
├── invoice.payment-tab.tpl
├── invoice.product-tab.tpl
├── invoice.shipping-tab.tpl
├── invoice.style-tab.tpl
├── invoice.summary-tab.tpl
├── invoice.tax-tab.tpl
├── invoice.total-tab.tpl
└── invoice.tpl

Merchandise Returns

Order (or Merchandise) Returns are available in Orders > Merchandise Returns section of the Back Office.

The following files are used to generate the PDF file:

pdf
├── order-return.addresses-tab.tpl
├── order-return.conditions-tab.tpl
├── order-return.product-tab.tpl
├── order-return.summary-tab.tpl
└── order-return.tpl

Order Slips

Order Slips are available in Orders > Order Slips section of the Back Office.

pdf
├── order-slip.payment-tab.tpl
├── order-slip.product-tab.tpl
├── order-slip.summary-tab.tpl
├── order-slip.total-tab.tpl
└── order-slip.tpl

Supply Orders

Since 1.7, Supply Orders are not available anymore in the Back Office but you can still “trigger” the generation of the PDF files.

pdf
├── supply-order.addresses-tab.tpl
├── supply-order-footer.tpl
├── supply-order-header.tpl
├── supply-order.product-tab.tpl
├── supply-order.tax-tab.tpl
├── supply-order.total-tab.tpl
└── supply-order.tpl

Common Templates

These files are used by most of the previous files:

pdf
├── footer.tpl
├── header.tpl
└── pagination.tpl

Minimal Example: customize design of the Invoices

{* /themes/your-theme/pdf/invoice.style-tab.tpl *}
{assign var=color_header value="#25B9D7"}
{assign var=color_border value="#3ED2F0"}
{assign var=color_border_lighter value="#DFF5F9"}
{assign var=color_line_even value="#FAFBFC"}
{assign var=color_line_odd value="#6C868E"}
{assign var=font_size_text value="12pt"}
{assign var=font_size_header value="12pt"}
{assign var=font_size_product value="12pt"}
{assign var=height_header value="25px"}
{assign var=table_padding value="7px"}

You will get an Invoice would look like this:

Stylized Invoice example

Alter or Add information available into PDF templates

A dynamic hook allows you to alter or add more information that will become available in the previous templates.

There is the list of available Template Classes in the Core:

  • HTMLTemplateDeliverySlip
  • HTMLTemplateInvoice
  • HTMLTemplateOrderReturn
  • HTMLTemplateOrderSlip
  • HTMLTemplateSupplyOrderForm

If your shop is available for the European Union, the GDPR module must be installed.

This means you can generate another PDF thanks to the class named HTMLTemplatePSGDPRModule. This PDF will contain all the Customer Information.

To guess the hook for each template, we can look at the HTMLTemplate abstract Class:

<?php
// l. 160
$template = ucfirst(str_replace('HTMLTemplate', '', get_class($this)));
$hook_name = 'displayPDF' . $template;

List of available Hooks

Concept Hook name
Invoice displayPDFInvoice
Invoice displayInvoiceLegalFreeText
Delivery Slip displayPDFDeliverySlip
Order Return displayPDFOrderReturn
Order Slip displayPDFOrderSlip
Supply Order displayPDFSupplyOrderForm
GDPR Archive displayPDFPSGDPRModule

Minimal Example: add an extra property to the Invoice

In modules/your-module/your-module.php:

<?php
public function hookDisplayPDFInvoice($hookArgs)
{
    $customer = $this->context->customer;
    $hookArgs['object']->is_birthday = $this->isBirthday($customer->birthday);
}

// Naive implementation: don't do that.
private function isBirthDay(string $customerBirthday)
{
    return date('y-m-d') == $customerBirthday;
}

In themes/your-theme/pdf/invoice.note-tab.tpl:

{* ... keep the content ... *}
{if $order_invoice->is_birthday}
    <tr>
        <td colspan="12">
            {l s='Happy Birthday !' d='Modules.YourModule.Pdf' pdf='true'}
        </td>
    </tr>
{/if}

You will get an Invoice would look like this:

Invoice with extra content example

Variables

For each of these templates, Smarty variables are available.

There is the list of the variables available:

  • {lastname}
  • {firstname}
  • {id_order}
  • {order_name}
  • {delivery_block_txt}
  • {invoice_block_txt}
  • {delivery_block_html}
  • {invoice_block_html}
  • {delivery_company}
  • {delivery_firstname}
  • {delivery_lastname}
  • {delivery_address1}
  • {delivery_address2}
  • {delivery_city}
  • {delivery_postal_code}
  • {delivery_country}
  • {delivery_state}
  • {delivery_phone}
  • {delivery_other}
  • {invoice_company}
  • {invoice_vat_number}
  • {invoice_firstname}
  • {invoice_lastname}
  • {invoice_address2}
  • {invoice_address1}
  • {invoice_city}
  • {invoice_postal_code}
  • {invoice_country}
  • {invoice_state}
  • {invoice_phone}
  • {invoice_other}
  • {order_name}
  • {date}
  • {carrier}
  • {payment}
  • {products}
  • {total_paid}
  • {total_products}
  • {total_discounts}
  • {total_shipping}
  • {total_wrapping}
  • {total_tax_paid}

They are set in the PaymentModule:validateOrder function.

This is also the right place to dump them and figure out what they provide.

Add or alter Smarty variables

As always, you can hook into this email workflow and while there are multiples hooks that can fit this specific needs, the best one is named actionGetExtraMailTemplateVars.

Once you get the right hook, you will be able to alter or add extra variables:

<?php
// your-module/your-module.php

public function install()
{
    ...
    $this->registerHook('actionGetExtraMailTemplateVars');
}

public function hookActionGetExtraMailTemplateVars($hookArgs)
{
    dump($hookArgs);
    // Adapted from PrestaShop Email Manager Module
    $hookArgs['extra_template_vars']['{password}'] = '*******'; 
}

Code example:

You can override the core PDF templates in your module.
  • Module files architecture

Assume that the .tpl files in the pdf folder have been copied from the Core

mycustompdfgenerator
├── pdf
    ├── delivery-slip.addresses-tab.tpl
    ├── delivery-slip.product-tab.tpl
    ├── delivery-slip.style-tab.tpl
    ├── delivery-slip.tpl
    ├── footer.tpl
    ├── header.tpl
├── mycustompdfgenerator.php
├── logo.png
  • PHP Code
   
<?php
if (!defined('_PS_VERSION_')) {
    exit;
}

class MyCustomPdfGenerator extends Module
{
    public function __construct()
    {
        $this->name = 'mycustompdfgenerator';
        $this->tab = 'front_office_features';
        $this->version = '1.0.0';
        $this->author = 'Firstname Lastname';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = [
            'min' => '1.7',
            'max' => _PS_VERSION_
        ];
        $this->bootstrap = true;
        parent::__construct();
        $this->displayName = $this->l('My module');
        $this->description = $this->l('Description of my module.');
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
    }
    
   
    public function generatePDF(array $params): void
    {
        $myOrderObject = new Order((int) $params['id_order']);
       
        $myCustomSlipVarsForPdfContent = $this->myContentDatasPresenter($myOrderObject);
        $myCustomSlipVarsForPdfFooter  = $this->myFooterDatasPresenter($myOrderObject);
        $myCustomSlipVarsForPdfHeader  = $this->myHeaderDatasPresenter($myOrderObject);
        $pdfGen = new PDFGenerator(false, 'P');
        $pdfGen->setFontForLang(Context::getContext()->language->iso_code);
        $pdfGen->startPageGroup();
        $pdfGen->createHeader($this->getHeader($myCustomSlipVarsForPdfHeader));
        $pdfGen->createFooter($this->getFooter($myCustomSlipVarsForPdfFooter));
        $pdfGen->createContent($this->getPdfContent($myCustomSlipVarsForPdfHeader));
        $pdfGen->writePage();
        $pdfGen->render('my_custom_pdf.pdf', 'D');
   }
   
   /**
     * Returns the template's HTML content.
     *
     * @return string HTML content
     */
    public function getPdfContent(array $myCustomSlipVarsForPdfContent): string
    {
        $this->context->smarty->assign($myCustomSlipVarsForPdfContent);

        $tpls = array(
            'style_tab'     => $this->context->smarty->fetch(__DIR__.'/pdf/delivery-slip.style-tab.tpl'),
            'addresses_tab' => $this->context->smarty->fetch(__DIR__.'/pdf/delivery-slip.addresses-tab.tpl'),
            'product_tab'   => $this->context->smarty->fetch(__DIR__.'/pdf/delivery-slip.product-tab.tpl'),
        );
        $this->context->smarty->assign($tpls);

        return $this->context->smarty->fetch(__DIR__.'/pdf/delivery-slip.tpl');
    }

   /**
     * Returns the template's HTML footer.
     *
     * @return string HTML footer
     */
    public function getFooter(array $myCustomSlipVarsForPdfFooter): string
    {
        $this->context->smarty->assign($myCustomSlipVarsForPdfFooter);
        return $this->context->smarty->fetch(__DIR__.'/pdf/footer.tpl');
    }

    /**
     * Returns the template's HTML header.
     *
     * @return string HTML header
     */
    public function getHeader(array $myCustomSlipVarsForPdfHeader): string
    {
        $this->context->smarty->assign($myCustomSlipVarsForPdfHeader);
        return $this->context->smarty->fetch(__DIR__.'/pdf/header.tpl');
    }
   
   
   /**
   * Format your order data here for pdf content : ['tpl_var_name'=>'tpl_value']
   *
   * @return array
   */
   public function myContentDatasPresenter(Order $myOrderObject): array
   {
     // TODO : implement it 
   }
   
   /**
   * Format your order data here for pdf footer : ['tpl_var_name'=>'tpl_value']
   *
   * @return array
   */
   public function myFooterDatasPresenter(Order $myOrderObject): array
   {
     // TODO : implement it 
   }
   
   /**
   * Format your order data here for pdf header : ['tpl_var_name'=>'tpl_value']
   *
   * @return array
   */
   public function myHeaderDatasPresenter(Order $myOrderObject): array
   {
     // TODO : implement it 
   }
}
  • Usage
 $myModuleOrderPdfGenerator = Module::getInstanceByName('mycustompdfgenerator');
 $myModuleOrderPdfGenerator->generatePDF([
    'id_order'=>666
  ]);