PDF Creation / PDF Mailer

Many business apps need a way to create PDF files. Invoices, empty forms for customers, reports etc. . So it is included in BPFW. The PDF templates can be written in html using mpdf and can be downloaded or automatically mailed to customers.

PDF generation is typically bound to another model. An invoice is bound to an order. A contract is bound to an user and so on. So in BPFW there is a way to add pdf generation to a model that can be accessed from the context menu or the dialog.


Hint: The topic is a bit hard to describe. The best way to learn how it is done is to look at the preinstalled example on examplecomplexmodel.inc.php and examplepdfmanagerModel.inc.php and use it as a template.

Hint2: While you can use html and css in both emails and the mpdf library that is used for pdf creation, only a subset is supported. So you don’t want to use something like a flex layout while rusty things like html tables work quite well.
For more Info visit https://mpdf.github.io/

Add PDF generation to a model

First create a new with a name of your choice. we will call it examplepdfmanagerModel.inc.php here.
As ab absolute minimum you have to add categories and the pdfs you want to create in initializeCategories().

We will create two categories that contain one pdf file each. You could also add multiple pdfs to one file.

<?php /** @noinspection PhpUnused */

require_once(BPFW_MVC_PATH."models/pdfmanagerModel.inc.php");
class ExamplepdfmanagerModel extends PdfmanagerModel
{

    protected function initializeCategories()
    {

        // define your pdfs / mails
        $this->addPdfCategory("invoice", "Invoice example", 0, RCPT_CUSTOMER, "Invoice", true);
        $this->addPdfCategory("customer_mail", "empty mail to customer", 0, RCPT_CUSTOMER, "info", true);

        // define the pdf parts
        $this->addPdfDocument("invoice_example", "Invoice", "invoice");
        $this->addPdfDocument("hours_made_pdf", "Hours made", "invoice");

    }

}


Add template files to project:
Now you need a few files in your %appname%/pdf folder that contain the contents of the pdf and in %appname%/mail that contain the contents of the mails that can be used to send the pdf.

invoice_example.inc.php -> optional way of do some customisation to the pdf or add custom variables.
Example:

<?php

require_once(BPFW_CORE_PATH."pdf/pdfpage.inc.php");

// you can do some settings if you create a class with the same name as the html page here
class invoice_example extends PdfPage
{

    /**
     * @param array $variables
     * @return array
     * @throws Exception
     */
    function getCustomVariablesForPage(array $variables) : array
    {

        $useridRcpt = $variables["examplecomplex.user"];

        $usermodel = bpfw_createModelByName("user");
        $userdata = $usermodel->GetEntry($useridRcpt);

// set rcpt userdata as "customer.xxxx" to be used in the pdf
        $variables = bpfw_setVariables($userdata, "customer", $variables);

        ob_start();
        ?>
        <style>
        #invoiceContent{
            width:100%;
        }
        #invoiceContent th{
            font-weight: bold;
        }
        </style>

<table id="invoiceContent">
    <tr>
        <td>Article</td>
        <td>Amount</td>
        <td>Single Price</td>
        <td>Price</td>
    </tr>
    <tr>
        <td colspan="4"><hr></td>
    </tr>
    <tr>
        <td>Article 1</td>
        <td>2</td>
        <td>50 €</td>
        <td>100 €</td>
    </tr>
    <tr>
        <td>Article 2</td>
        <td>1</td>
        <td>150 €</td>
        <td>150 €</td>
    </tr>
    <tr>
        <td>Article 3</td>
        <td>3</td>
        <td>20 €</td>
        <td>60 €</td>
    </tr>

    <tr>
        <td colspan="4"><hr></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td>Sum net</td>
        <td>310 €</td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td>Taxes</td>
        <td>61 €</td>
    </tr>
    <tr>
        <td colspan="2"> </td>
        <td colspan="2"><hr></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td style="font-weight: bold">Total</td>
        <td style="font-weight: bold">371 €</td>
    </tr>
</table>

<?php
        $variables["invoiceContent"] = ob_get_clean();

        return parent::getCustomVariablesForPage($variables);
    }

}

signature.html : signature of the mail that is sent here and in massmailer
customer_mail.html and invoice.html: Text of the mails
customer_mailTitle.html and invoiceTitle.html: Title of the mails
defaultfooter.html, defaultheader.html, whiteheader.html -> used in all the pdfs if enabled
hours_made_pdf.html and invoide_example.html -> the html template that is used to create the pdf utilizing
mpdf
mailtext.html – > the mailtext is the first page of the pdf if not disabled. maitext.html is wrapped around it and should contain {{{common/mailtext}}}. That’s where the mailtext is put in.

All those file can contain html text and placeholders in 3x curly braces like {{{user.firstname }}}.
You can add all variables of the used model in there like {{{invoide.invoice_no}}}

There are also a few predefined variables:
{{{common/mailtext}}} -> mailtext
{{{common/pdfstyles}}} -> include the stylesheet
{{{common/header}}} -> include the header
{{{common/footer}}} -> include the footer

{{{BASE_URI}}} -> url for links
{{{BPFW_WWW_URI}}} -> url to www folder of th framework
{{{APP_IMGS_PATH}}} -> path (NOT uri) to images folder of the app
{{{PARENT_IMGS_PATH}}} -> path (NOT uri) to images folder of the parent app
{{{APP_IMGS_URI}}} -> if you want to include images with <img.. , path to app images folder
{{{PARENT_IMGS_URI}}} -> if you want to include images, path to parent images folder

{{{currentDate}}} -> the current date, useful for sinatures and invoices
{{{currentuser.(variables of usermodel)}}}-> all variables of the current user like currentuser.firstname or currentuser.email

Adding the buttons the the parent model/view:

Now add the button to the contextmenu and/or the form by adding this to the associated view (examplecomplexmodelView.inc.php):

    // method to add to the context menu
    function extraButtons($key, $value)
    {
        if (bpfw_isAdmin()) { // optional if only admins should see the buttons
            // open as popup window
            echo '
            <a class="tableicon_size open_eventmanager iframepopup" data-id=' . $key . ' title="pdf generation and sending" href="?p=examplepdfmanager&modelused=examplecomplex&hideNavigation=true&filter=' . $key . '">
                <i class="tableicon fas fa-file-pdf">Create .pdf</i>
            </a>
            ';
        }
    }

    // method to add the button to the add/edit/duplicate forms
    function actionBeforeCloseIcon($rowId): void
    {

        if (bpfw_isAdmin()) { // optional if only admins should see the buttons
            ?>
            <span class="headerdialogbuttonrow datalist_button_wrapper list-enabled">
                <button type="button" class="tableicon_size open_eventmanager"
                        onclick="startIFrameDialog(<?php echo $rowId; ?>, '?p=examplepdfmanager&modelused=examplecomplex&hideNavigation=true&filter=<?php echo $rowId; ?>');"
                        data-id="<?php echo $rowId; ?>">
                    <i class="tableicon fas fa-file-pdf"></i>
                </button>  
            </span>

            <?php

        }

    }


    function initializeVariables()
    {

        parent::initializeVariables();

        // filter to add button to list
        bpfw_add_action(DefaultlistView::ACTION_DEFAULTLISTVIEW_DISPLAY_BUTTONS, $this->model->GetTableName(), array($this, "extraButtons"), 10, 2);

        // filter to add buton to the forms
        bpfw_add_action(DefaultlistView::ACTION_EDITDIALOG_BEFORE_HEADER_CLOSE_ICON, $this->model->GetTableName(), array($this, "actionBeforeCloseIcon"), 10, 1);

    }

If everything was done correctly, you should have something like that:

Two other essential things you can do after that:

Default attachments:

They can be added with Administrator -> E-mail attachments

And show up on the right side of the pdf mailer.
This way you can easily add something like advertising material or a blank contract form.

Fonts:

To add fonts, put them into the %appdata%/www/fonts path.
Then override the getCustomFonts() method in your pdfmodel constructor.
Then you can use them with css or define the default font with getDefaultFont()
For more details read https://mpdf.github.io/fonts-languages/fonts-in-mpdf-7-x.html

Example:

class ExamplepdfmanagerModel extends PdfmanagerModel
{
    // [..]
    public function getCustomFonts($fontData){
        $customfontdata =
            [
                'avenir' => [
                    'R' => 'Avenir'.DIRECTORY_SEPARATOR.'12 AVENIR 65 MEDIUM 06173.TTF',
                    'B' => 'Avenir'.DIRECTORY_SEPARATOR.'12 AVENIR 85 HEAVY 08173.TTF',
                ],
            ];

        return array_merge($fontData, $customfontdata);

    }

    public function getDefaultFont(string $defaultFont) : string{
        return 'avenir';
    }

}
WordPress Cookie Plugin by Real Cookie Banner