1

Has anyone added a customer email address to the PDF invoice in a recent version of Magento, there is a nice example for an older version from Elgentos here:

How do I add a customer's email to the sales invoice?

I am pretty new to Magento, but I can see the module needs registering in a different way etc for recent versions and I am unsure if it can even be taken across.

1

I know its bad form to post an answer to your own question, but it may help others who wish to add the email to the address.

Add di.xml in modules app/code/myvendorname/mymodulename/etc directory:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Customer\Block\Address\Renderer\DefaultRenderer"
 type="myvendorname\mymodulename\Block\Address\Renderer\DefaultRenderer"/> 
</config>

Add module.xml to the same directory:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="mymodulename" setup_version="1.0.0">
</module>
</config>

Add registration.php to the app/code/myvendorname/mymodulename directory:

<?php 
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'mymodulename',
__DIR__
);

Add defaultrender.php to app/code/myvendorname/mymodulename/Block/Address/Renderer/

<?php
namespace myvendorname/mymodulename\Block\Address\Renderer;

use Magento\Customer\Model\Address\Mapper; use 
Magento\Customer\Model\Metadata\ElementFactory;

class DefaultRenderer extends 
\Magento\Customer\Block\Address\Renderer\DefaultRenderer {

protected $_customerMetadataService;

public function __construct(
\Magento\Framework\View\Element\Context $context,
ElementFactory $elementFactory,
\Magento\Directory\Model\CountryFactory $countryFactory,
\Magento\Customer\Api\AddressMetadataInterface $metadataService,
Mapper $addressMapper,
\Magento\Customer\Api\CustomerMetadataInterface $customerMetadataService,
array $data = []
)
{
parent::__construct($context, $elementFactory, $countryFactory, 
$metadataService, $addressMapper, $data);
$this->_customerMetadataService = $customerMetadataService;
}

/**
* {@inheritdoc}
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function renderArray($addressAttributes, $format = null)
{
switch ($this->getType()->getCode()) {
    case 'html':
        $dataFormat = ElementFactory::OUTPUT_FORMAT_HTML;
        break;
    case 'pdf':
        $dataFormat = ElementFactory::OUTPUT_FORMAT_PDF;
        break;
    case 'oneline':
        $dataFormat = ElementFactory::OUTPUT_FORMAT_ONELINE;
        break;
    default:
        $dataFormat = ElementFactory::OUTPUT_FORMAT_TEXT;
        break;
}

$attributesMetadata = $this->_addressMetadataService->getAllAttributesMetadata();
if (null !== $this->_customerMetadataService->getAttributeMetadata('email'))
{
$attributesMetadata[] = $this->_customerMetadataService->getAttributeMetadata('email');
}

$data = [];
foreach ($attributesMetadata as $attributeMetadata) {
    if (!$attributeMetadata->isVisible()) {
        continue;
    }
    $attributeCode = $attributeMetadata->getAttributeCode();
    if ($attributeCode == 'country_id' && isset($addressAttributes['country_id'])) {
        $data['country'] = $this->_countryFactory->create()->loadByCode(
            $addressAttributes['country_id']
        )->getName();
    } elseif ($attributeCode == 'region' && isset($addressAttributes['region'])) {
        $data['region'] = __($addressAttributes['region']);
    } elseif (isset($addressAttributes[$attributeCode])) {
        $value = $addressAttributes[$attributeCode];
        $dataModel = $this->_elementFactory->create($attributeMetadata, $value, 'customer_address');
        $value = $dataModel->outputValue($dataFormat);
        if ($attributeMetadata->getFrontendInput() == 'multiline') {
            $values = $dataModel->outputValue(ElementFactory::OUTPUT_FORMAT_ARRAY);
            // explode lines
            foreach ($values as $k => $v) {
                $key = sprintf('%s%d', $attributeCode, $k + 1);
                $data[$key] = $v;
            }
        }
        $data[$attributeCode] = $value;
    }
}
if ($this->getType()->getEscapeHtml()) {
    foreach ($data as $key => $value) {
        $data[$key] = $this->escapeHtml($value);
    }
}
$format = $format !== null ? $format : $this- 
>getFormatArray($addressAttributes);
return $this->filterManager->template($format, ['variables' => $data]);
}
}

After the usual upgrade process you can then add email to your pdf address template in the Magento backend:

 {{depend email}}Email: {{var email}}{{/depend}}
0

Firstly, a little bit of theory. The following model's function getPdf renders invoice's data:

vendor/magento/module-sales/Model/Order/Pdf/Invoice.php

To be more specific, the function insertOrder of the parent's class

vendor/magento/module-sales/Model/Order/Pdf/AbstractPdf.php

adds customer's data to the final PDF.

So, in order to write a customer's email in the Invoice's pdf, we can do the following:

1) Create di.xml inside of your module's etc directory:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Sales\Model\Order\Pdf\Invoice" type="YourVendor\YourModule\Model\Order\Pdf\Invoice" />
</config>

2) Create the model YourVendor\YourModule\Model\Order\Pdf\Invoice:

<?php
namespace YourVendor\YourModule\Model\Order\Pdf;

class Invoice extends \Magento\Sales\Model\Order\Pdf\Invoice
{
    protected function insertOrder(&$page, $obj, $putOrderId = true)
    {
        parent::insertOrder($page, $obj, $putOrderId);

        /**
         * Here we add the customer's email to the pdf
         */
        $order = $obj;
        $x = 450;
        $y = 705;
        $page->drawText($order->getShippingAddress()->getEmail(), $x, $y, 'UTF-8');
    }
}

3) Flush the cache.

That's it! Now, when you print your invoice, you'll see a neat customer's email writing at the top of the PDF.

You can move the writing wherever you like by changing x and y values.

Note: I've just tested my code in M2 CE 2.2.6, it works nicely there.

  • Thank you so much for the proposed solution Nikita, but I would really like a solution that extended the customer address, in that way all the customers contact data would be grouped together and the framing would adjust to the extra data. – ChrisC Oct 15 '18 at 11:56
  • Okay, Chris, I'll provide you updates a little bit later tonight – Nikita Abrashnev Oct 15 '18 at 16:34
  • Chris - please try now. I've updated the email's position to be inside of the shipping address box. – Nikita Abrashnev Oct 15 '18 at 19:22
  • Nikita - does this work in production mode? I don't see any changes when trying the code. Don't you need to create a module like this example: (magento.stackexchange.com/questions/122287/…) – ChrisC Oct 22 '18 at 14:37
  • @ChrisC - yep, you do need to create a new module. My example is based on creation of a new module, then creating di.xml and a new model in it. You never have to change files in the vendor directory manually. – Nikita Abrashnev Oct 22 '18 at 14:45
0

I tried your solution ChrisC, but I get an error in console after uploading the plugin:

[Symfony\Component\Console\Exception\CommandNotFoundException] There are no commands defined in the "cache" namespace.

If it works on your Magento, can you maybe provide the plugin files e.g. on Github? Would appreciate it! Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.