Announcement

Collapse
No announcement yet.

Custom Template PDF File Names

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom Template PDF File Names

    I don't know if this is of interest to anyone but I thought I'd share it.

    As we are implementing more and more business functions within EspoCRM, we are producing more and more Templates which are ultimately being turned into PDF files (orders, job sheets, inventory, etc, etc). Once of the needs we have is to be able to generate a PDF from a template and save it to our company network with a given file name format (depending on the type of information within the file).

    Natively, EspoCRM doesn't seem to allow you to be able to generate a file name format for each individual entity so I've managed to bake my own system. Its not upgrade-proof but for us, that does not matter. Someone may want to take this forward to make it so.

    The following implementation is what I did to allow us to generate file names specific to an entity:

    1. Edit the application\Espo\Services\Pdf.php file, replacing the buildFromTemplate function with below

    Code:
    public function buildFromTemplate(Entity $entity, Entity $template, $displayInline = false)
    {
        $entityType = $entity->getEntityType();
        // Get the name field from the entity for a default file name
        $name = $entity->get('name');
    
        if ($this->getServiceFactory()->checkExists($entityType)) {
            $service = $this->getServiceFactory()->create($entityType);
        } else {
            $service = $this->getServiceFactory()->create('Record');
        }
    
        $service->loadAdditionalFields($entity);
    
        if (method_exists($service, 'loadAdditionalFieldsForPdf')) {
            $service->loadAdditionalFieldsForPdf($entity);
        }
    
        // Pull the file name from the service, if the function exists
        if (method_exists($service, 'getFileNameForPdf')) {
            $name = $service->getFileNameForPdf($entity);
        }
    
        if ($template->get('entityType') !== $entityType) {
            throw new Forbidden();
        }
    
        if (!$this->getAcl()->check($entity, 'read') || !$this->getAcl()->check($template, 'read')) {
            throw new Forbidden();
        }
    
        $htmlizer = $this->createHtmlizer();
        $pdf = new \Espo\Core\Pdf\Tcpdf();
    
        $this->printEntity($entity, $template, $htmlizer, $pdf);
    
        if ($displayInline) {
            $name = \Espo\Core\Utils\Util::sanitizeFileName($name);
            $fileName = $name . '.pdf';
    
            $pdf->output($fileName, 'I');
            return;
        }
    
        return $pdf->output('', 'S');
    }
    The main change above is that a service function called `getFileNameForPdf` will get called if it exists.

    Next, we need to go into the relevant `Services\EntityName.php` file and add the function as below:

    Code:
    public function getFileNameForPdf(Entity $entity)
    {
        return $entity->get('name') . ' - ' . $entity->get('accountName');
    }
    And voila, we now have customisable file names on an Entity-by-Entity basis.

  • #2
    Thanks for sharing blueprint !

    Here's a possible way to make your customization "update-safe" although a little elaborate:

    1) create new view client/custom/src/views/record/detail.js
    Code:
    define('custom:views/record/detail', ['views/record/detail'], function (Dep, ViewRecordHelper) {
    
        return Dep.extend({
    
            actionPrintPdf: function () {
                this.createView('pdfTemplate', 'views/modals/select-template', {
                    entityType: this.model.name
                }, function (view) {
                    view.render();
                    this.listenToOnce(view, 'select', function (model) {
                        this.clearView('pdfTemplate');
                        window.open('?entryPoint=pdfCustomFileName&entityType='+this.model.name+'&entityId='+this.model.id+'&templateId=' + model.id, '_blank');
                    }, this);
                });
            }
    
        });
    });
    2) create new entry point file custom/Espo/Custom/EntryPoints/PdfCustomFileName.php
    Code:
    <?php
    
    namespace Espo\Custom\EntryPoints;
    
    use \Espo\Core\Exceptions\NotFound;
    use \Espo\Core\Exceptions\BadRequest;
    
    class PdfCustomFileName extends \Espo\Core\EntryPoints\Base
    {
        public static $authRequired = true;
    
        // default action
        public function run()
        {
    
            if (empty($_GET['entityId']) || empty($_GET['entityType']) || empty($_GET['templateId'])) {
                throw new BadRequest();
            }
            $entityId = $_GET['entityId'];
            $entityType = $_GET['entityType'];
            $templateId = $_GET['templateId'];
            $isPortal = $_GET['isPortal'];
    
            $entity = $this->getEntityManager()->getEntity($entityType, $entityId);
            $template = $this->getEntityManager()->getEntity('Template', $templateId);
    
            if (!$entity || !$template) {
                throw new NotFound();
            }
    
            $this->getContainer()->get('serviceFactory')->create('PdfCustomFileNameService')->buildFromTemplate($entity, $template, $isPortal);
    
            exit;
        }
    }
    3) Create new service file custom/Espo/Custom/Services/PdfCustomFileNameService.php
    Code:
    namespace Espo\Custom\Services
    use \Espo\Core\Exceptions\Forbidden;
    use \Espo\Core\Exceptions\NotFound;
    use \Espo\Core\Exceptions\Error;
    use \Espo\ORM\Entity;
    //use \Espo\Core\Htmlizer\Htmlizer;
    use \Espo\Custom\Htmlizer\CustomHtmlizer;
    
    class PdfCustomFileNameService extends \Espo\Core\Services\Base
    {
    // copy the entire contents of your modified application\Espo\Services\Pdf.php file
    }
    4) Not sure why but when I invoke the standard Htmilizer file from the custom service above I get an error :-( ??? (if anyone can provide some guidance it will be highly appreciated it), so I commented this line and created a "CustomHtmilizer" custom/Espo/Custom/Htmilzer/CustomHtmiler.php that essentially clones Espo\Core\Htmlizer\Htmlizer (see the code above)
    Code:
    namespace Espo\Custom\Htmlizer;
    
    class CustomHtmlizer extends \Espo\Core\Htmlizer\Htmlizer
    {
    
    }
    5) Create a custom clientDef file for each entity class where you want to have the custom pdf capability and specify the custom record view there (for example at custom/Espo/Custom/Resources/metadata/clientDefs/Contact.json)
    Code:
    {
        "recordViews": {
            "detail": "custom:views/record/detail"
        }
    }
    Last edited by telecastg; 01-16-2020, 02:28 AM.

    Comment


    • #3
      Originally posted by telecastg View Post

      Here's a possible way to make your customization "update-safe" although a little elaborate:
      Nice one, that's perfect!

      Comment


      • #4
        This is awesome, looking forward to a pull request on the Git.

        Off-topic: How you you even make good looking PDF template with the current PDF library? All my PDF doesn't look the way it is formated in the Template design.
        Last edited by espcrm; 01-17-2020, 03:44 AM.

        Comment


        • #5
          Originally posted by espcrm View Post
          Off-topic: How you you even make good looking PDF template with the current PDF library? All my PDF doesn't look the way it is formatted in the Template design.
          Honestly, I couldn't. I became very frustrated with TCPDF and decided that the best way was to print everything to the main section of Espo screen, to take advantage of the existing css styles or to define my own styles and display in a new window and then "print" using my browser's PDF engine, so I can get a document exactly as I styled it.

          It could be that I just don't get how TCPDF can work, but I decided to move on since I had accomplished my goal.

          Comment


          • espcrm
            espcrm commented
            Editing a comment
            Yes, the current Print through web browser is pretty good. Perhaps we need the Report function to property print information.

        • #6
          Hello,
          other possibility (i need word) .. i use phpOffice library :
          i create a template (word docx) .. i put in folder ..
          so with phpWord, i read template docx .. inject all data to docx and output as pdf or docx if needed.

          Regards

          Comment


          • espcrm
            espcrm commented
            Editing a comment
            I'm a bit confused. Is this what you are doing currently or what you are hoping to be able to do.

            I haven't find any Open Source CRM that is capable of Mailmerge and producing Word type of template. It always as PDF, most of which I can never make it look good or work as intended (could not merge the data I want).

        • #7
          Originally posted by espcrm View Post
          Off-topic: How you you even make good looking PDF template with the current PDF library? All my PDF doesn't look the way it is formated in the Template design.
          Well, it actually is possible - it just takes a lot of tinkering time.

          The CSS rules are really really hit-and-miss but once you get used to the foibles of the TCPDF library, you can create some really decent templates. That said, it is a pain in the backside and takes far far longer (especially when you have to create really nice public-facing PDFs) so any 3rd party implementation of a better PDF engine can't come soon enough.

          Comment

          Working...
          X