0

I am new to Magneto. and i searched and tried different solution online but they didn't work. I have an observer from which I generate CSV file whenever a customer place an order. I'm using <sales_order_place_after> to get it triggered. now i want to add the code to send the email with the generated CSV file in attachment to the customer. i'm getting this error: Unable to send mail: Unknown error {"exception":"[object] (Laminas\Mail\Transport\Exception\RuntimeException(code: 0): Unable to send mail: Unknown error at /home/lenovo/Ven/vendor/laminas/laminas-mail/src/Transport/Sendmail.php:298)"} []

Storeven/Orders/Observers/OrderExport.php

<?php
namespace Storeven\Orders\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\Store\Model\ScopeInterface;

use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\Translate\Inline\StateInterface;

class Orderexport implements ObserverInterface
{
//changes

protected $_objectManager;
private $logger;
private $productFactory;
//-----//
protected $_transportBuilder;
protected $inlineTranslation;

protected $_request;
protected $_order;
protected $_productRepository;
protected $_scopeConfig;
protected $_customer;
protected $_storemanager;
protected $_dir;
public function __construct(
\Magento\Framework\App\RequestInterface $request,
\Magento\Sales\Model\Order $order,
\Magento\Framework\App\Response\Http\FileFactory $fileFactory,
Filesystem $filesystem,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Customer\Model\Customer $customer,
\Magento\Store\Model\StoreManagerInterface $storemanager,

\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Framework\ObjectManager\ObjectManager $objectManager,

\Psr\Log\LoggerInterface $logger,
\Magento\Catalog\Model\ProductFactory $productFactory,

\Magento\Catalog\Model\ProductRepository $productRepository,

\Magento\Framework\Filesystem\DirectoryList $dir,

\Magento\Framework\Mail\Template\TransportBuilder $transportBuilder 

) { 
$this->_scopeConfig = $scopeConfig;
$this->_customer = $customer;
$this->_storemanager = $storemanager;
$this->_request = $request;
$this->_order = $order;
$this->_fileFactory = $fileFactory;
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
$this->_productRepository = $productRepository;
$this->_transportBuilder = $transportBuilder;
$this->logger = $logger;
$this->dir = $dir;
}
public function getRootDirectory()
{
return $this->dir->getRoot();
}
public function execute(\Magento\Framework\Event\Observer $observer) {
$order = $observer->getEvent()->getOrder();

$store = $this->_storemanager->getStore()->getName();

$websiteID = $this->_storemanager->getStore()->getWebsiteId();
$headers = array('Company Name', 'Contact Name', 'Contact Email', 'Contact Phone','Shipping Address' ,'SKU','ProductColorID','QTY','Price','Total','CBM','Weight');

$name = strtotime('now');
$file = 'customorderexport/'.$name.'_detailed_orderexport.csv';
$this->directory->create('customorderexport');
$stream = $this->directory->openFile($file, 'w+');
$stream->lock();
$stream->writeCsv($headers);
$orderdetail['Company Name'] = "";
$orderdetail['Contact Name'] = $order->getCustomerName();
$orderdetail['Contact Email'] = $order->getCustomerEmail();
$orderdetail['Contact Phone'] = $order->getShippingAddress()->getTelephone();
$streetadd = $order->getShippingAddress()->getStreet();
$orderdetail['Shipping Address'] = $streetadd[0];


$items = $order->getAllItems();
foreach ($items as $item) {

$orderdetail['SKU'] = $item->getSKU();
$orderdetail['ProductColorID']= "";
$orderdetail['QTY'] = $item->getQtyOrdered();
$orderdetail['Price'] = $item->getPrice();
$orderdetail['Total'] = $item->getRowTotalInclTax();

// $id = $item->getProdcutId();
// $product = $this->_productRepository->getById($id);

// $orderdetail['CBM'] = $product->getcbm();
$orderdetail['CBM'] = "";

$quantity = $item->getQtyOrdered();
$orderdetail['Weight'] = $item->getWeight() * $quantity ;
$stream->writeCsv($orderdetail);
}

$stream->unlock();
$stream->close();

//Email code
//$Email_template_name="AddEmailAttachemnt";
// $file_url = 'storeven/var/customorderexport/'.$name.'_detailed_orderexport.csv';
try {
$file_url = '/home/lenovo/storeven/var/customorderexport/1654160951_detailed_orderexport.csv';
$emailTemplateVariables['message'] = 'This is a test message.';
$file_content = file_get_contents($file_url);
$file_name = $name."_detailed_orderexport.csv";
$extension="text/csv";
$transport = $this->_transportBuilder->setTemplateIdentifier('myemail_email_template') 

->setTemplateVars($emailTemplateVariables)
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => $this->_storemanager->getStore()->getId()
]) 
->setFrom([
'email' => '[email protected]',
'name' => 'my store'
])
->addTo('[email protected]') // Receiver Email Address
->addAttachment($file_content, $file_name, $extension) // here Addtement are add with Email
->getTransport();


}
catch (\Exception $e) {
$this->logger->critical($e->getMessage());
}
}


}

Orders/Model/Mail/Template/AddEmailAttachemnt.php


namespace Storeven\Orders\Model\Mail\Template;

use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\MessageInterfaceFactory;
use Magento\Framework\Mail\MimeInterface;
use Magento\Framework\Mail\MimeMessageInterfaceFactory;
use Magento\Framework\Mail\MimePartInterfaceFactory;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Laminas\Mime\Mime;
use Laminas\Mime\PartFactory;

class AddEmailAttachemnt extends \Magento\Framework\Mail\Template\TransportBuilder
{

protected $templateIdentifier;
protected $templateModel;
protected $templateVars;
protected $templateOptions;
protected $transport;
protected $templateFactory;
protected $objectManager;
protected $message;
protected $_senderResolver;
protected $mailTransportFactory;
private $messageData = [];
private $emailMessageInterfaceFactory;
private $mimeMessageInterfaceFactory;
private $mimePartInterfaceFactory;
private $addressConverter;
protected $attachments = [];
protected $partFactory;

public function __construct(
FactoryInterface $templateFactory,
MessageInterface $message,
SenderResolverInterface $senderResolver,
ObjectManagerInterface $objectManager,
TransportInterfaceFactory $mailTransportFactory,
MessageInterfaceFactory $messageFactory = null,
EmailMessageInterfaceFactory $emailMessageInterfaceFactory = null,
MimeMessageInterfaceFactory $mimeMessageInterfaceFactory = null,
MimePartInterfaceFactory $mimePartInterfaceFactory = null,
AddressConverter $addressConverter = null
) {
$this->templateFactory = $templateFactory;
$this->objectManager = $objectManager;
$this->_senderResolver = $senderResolver;
$this->mailTransportFactory = $mailTransportFactory;
$this->emailMessageInterfaceFactory = $emailMessageInterfaceFactory ?: $this->objectManager
->get(EmailMessageInterfaceFactory::class);
$this->mimeMessageInterfaceFactory = $mimeMessageInterfaceFactory ?: $this->objectManager
->get(MimeMessageInterfaceFactory::class);
$this->mimePartInterfaceFactory = $mimePartInterfaceFactory ?: $this->objectManager
->get(MimePartInterfaceFactory::class);
$this->addressConverter = $addressConverter ?: $this->objectManager
->get(AddressConverter::class);
$this->partFactory = $objectManager->get(PartFactory::class);
parent::__construct(
$templateFactory,
$message,
$senderResolver,
$objectManager,
$mailTransportFactory,
$messageFactory,
$emailMessageInterfaceFactory,
$mimeMessageInterfaceFactory,
$mimePartInterfaceFactory,
$addressConverter
);
}
public function addCc($address, $name = '')
{
$this->addAddressByType('cc', $address, $name);

return $this;
}
public function addTo($address, $name = '')
{
$this->addAddressByType('to', $address, $name);

return $this;
}
public function addBcc($address)
{
$this->addAddressByType('bcc', $address);

return $this;
}
public function setReplyTo($email, $name = null)
{
$this->addAddressByType('replyTo', $email, $name);

return $this;
}
public function setFrom($from)
{
return $this->setFromByScope($from);
}
public function setFromByScope($from, $scopeId = null)
{
$result = $this->_senderResolver->resolve($from, $scopeId);
$this->addAddressByType('from', $result['email'], $result['name']);

return $this;
}
public function setTemplateIdentifier($templateIdentifier)
{
$this->templateIdentifier = $templateIdentifier;

return $this;
}
public function setTemplateModel($templateModel)
{
$this->templateModel = $templateModel;
return $this;
}
public function setTemplateVars($templateVars)
{
$this->templateVars = $templateVars;

return $this;
}
public function setTemplateOptions($templateOptions)
{
$this->templateOptions = $templateOptions;

return $this;
}
public function getTransport()
{
try {
$this->prepareMessage();
$mailTransport = $this->mailTransportFactory->create(['message' => clone $this->message]);
} finally {
$this->reset();
}

return $mailTransport;
}
protected function reset()
{
$this->messageData = [];
$this->templateIdentifier = null;
$this->templateVars = null;
$this->templateOptions = null;
return $this;
}
protected function getTemplate()
{
return $this->templateFactory->get($this->templateIdentifier, $this->templateModel)
->setVars($this->templateVars)
->setOptions($this->templateOptions);
}
protected function prepareMessage()
{
$template = $this->getTemplate();
$content = $template->processTemplate();
switch ($template->getType()) {
case TemplateTypesInterface::TYPE_TEXT:
$part['type'] = MimeInterface::TYPE_TEXT;
break;

case TemplateTypesInterface::TYPE_HTML:
$part['type'] = MimeInterface::TYPE_HTML;
break;

default:
throw new LocalizedException(
new Phrase('Unknown template type')
);
}
$mimePart = $this->mimePartInterfaceFactory->create(['content' => $content]);
$parts = count($this->attachments) ? array_merge([$mimePart], $this->attachments) : [$mimePart];
$this->messageData['body'] = $this->mimeMessageInterfaceFactory->create(
['parts' => $parts]
);

$this->messageData['subject'] = html_entity_decode(
(string) $template->getSubject(),
ENT_QUOTES
);
$this->message = $this->emailMessageInterfaceFactory->create($this->messageData);

return $this;
}
private function addAddressByType($addressType, $email, $name = null): void
{
if (is_string($email)) {
$this->messageData[$addressType][] = $this->addressConverter->convert($email, $name);
return;
}
$convertedAddressArray = $this->addressConverter->convertMany($email);
if (isset($this->messageData[$addressType])) {
$this->messageData[$addressType] = array_merge(
$this->messageData[$addressType],
$convertedAddressArray
);
}
}
public function addAttachment($content, $fileName, $fileType)
{
$attachmentPart = $this->partFactory->create();
$attachmentPart->setContent($content)
->setType($fileType)
->setFileName($fileName)
->setDisposition(Mime::DISPOSITION_ATTACHMENT)
->setEncoding(Mime::ENCODING_BASE64);
$this->attachments[] = $attachmentPart;

return $this;
}
}

2 Answers 2

1

Please to be sure you have a correct template for your custom email

app/code/Acme/StackExchange/etc/email_templates.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd">
    <template id="myemail_email_template" label="export_email_template" file="export_email_template.html" type="html" module="Acme_StackExchange" area="frontend"/>
</config>

app/code/Acme/StackExchange/view/frontend/email/export_email_template.html

<!--@subject {{trans "StackExchange Email with Attachment"}} @-->
<!--@vars {} @-->

{{template config_path="design/email/header_template"}}

{{template config_path="design/email/footer_template"}}

Working example based on your code with some quick refactoring

app/code/Acme/StackExchange/Observer/Sales/OrderPlaceAfter.php

<?php
declare(strict_types=1);

namespace Acme\StackExchange\Observer\Sales;

use Laminas\Mime\Mime as MimeType;
use Laminas\Mime\Message as MimeMessage;
use Laminas\Mime\Part as MimePart;
use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Filesystem;
use Magento\Framework\Mail\Template\TransportBuilderFactory;
use Magento\Sales\Model\Order;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;

class OrderPlaceAfter implements ObserverInterface
{
    private const EXPORT_DIR = 'customorderexport';

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * @var Filesystem
     */
    protected $filesystem;

    /**
     * @var TransportBuilderFactory
     */
    protected $transportBuilderFactory;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    public function __construct(
        StoreManagerInterface $storeManager,
        ScopeConfigInterface $scopeConfig,
        Filesystem $filesystem,
        TransportBuilderFactory $transportBuilderFactory,
        LoggerInterface $logger
    ) {
        $this->storeManager            = $storeManager;
        $this->scopeConfig             = $scopeConfig;
        $this->filesystem              = $filesystem;
        $this->transportBuilderFactory = $transportBuilderFactory;
        $this->logger                  = $logger;
    }

    /**
     * @inheritDoc
     */
    public function execute(Observer $observer)
    {
        /** @var Order $order */
        $order = $observer->getEvent()->getData('order');

        try {
            $fileContent = $this->getOrderDetailCsv($order);
            $fileType    = 'text/csv';
            $fileName    = 'detailed_orderexport.csv';
            $storeId     = $this->storeManager->getStore()->getId();
            $sender      = 'sales_email/order/identity';

            $transportBuilder = $this->transportBuilderFactory->create();
            $transportBuilder
                ->setTemplateIdentifier('myemail_email_template')
                ->setTemplateOptions(['area' => Area::AREA_FRONTEND, 'store' => $storeId])
                ->setTemplateVars([])
                ->setFromByScope($this->scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId), $storeId)
                ->addTo('[email protected]');
            $transport = $transportBuilder->getTransport();

            $body = $transport->getMessage()->getBody();
            if ($body instanceof MimeMessage) {
                $parts = $body->getParts();

                $attachmentPart = new MimePart();
                $attachmentPart->setContent($fileContent)
                    ->setType($fileType)
                    ->setFileName($fileName)
                    ->setDisposition(MimeType::DISPOSITION_ATTACHMENT)
                    ->setEncoding(MimeType::ENCODING_BASE64);
                $parts[] = $attachmentPart;

                $message = new MimeMessage();
                $message->setParts($parts);

                $transport->getMessage()->setBody($message);
            }

            $transport->sendMessage();
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    private function getOrderDetailCsv(Order $order): string
    {
        $name      = $order->getIncrementId();
        $file      = self::EXPORT_DIR . '/' . $name . '.csv';
        $directory = $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
        $directory->create(self::EXPORT_DIR);
        $stream = $directory->openFile($file, 'w+');

        $stream->lock();
        $headers = ['Company Name', 'Contact Name', 'Contact Email', 'Contact Phone', 'Shipping Address', 'SKU', 'ProductColorID', 'QTY', 'Price', 'Total', 'CBM', 'Weight'];
        $stream->writeCsv($headers);

        $csvRow = [
            'Company Name'     => '',
            'Contact Name'     => $order->getCustomerName(),
            'Contact Email'    => $order->getCustomerEmail(),
            'Contact Phone'    => $order->getShippingAddress()->getTelephone(),
            'Shipping Address' => $order->getShippingAddress()->getStreetLine(1),
        ];

        foreach ($order->getAllVisibleItems() as $item) {
            $csvRow['SKU']            = $item->getSKU();
            $csvRow['ProductColorID'] = '';
            $csvRow['QTY']            = $item->getQtyOrdered();
            $csvRow['Price']          = $item->getPrice();
            $csvRow['Total']          = $item->getRowTotalInclTax();
            $csvRow['CBM']            = "";
            $csvRow['Weight']         = $item->getWeight() * $item->getQtyOrdered();

            $stream->writeCsv($csvRow);
        }

        $stream->unlock();
        $stream->close();

        return $directory->readFile($file);
    }
}

Result

2
  • I'm getting this Email template 'myemail_email_template' is not defined. [] [] Commented Jun 3, 2022 at 13:47
  • Please see carefully example of email_templates.xml. I've used myemail_email_template definition from your code. Don't forget to update email_templates.xml and specify correct module and template path and clear cache. Commented Jun 3, 2022 at 13:58
0

This is the method where your code is failing

/**
 * Send mail using PHP native mail()
 *
 * @param  string $to
 * @param  string $subject
 * @param  string $message
 * @param  string $headers
 * @param  $parameters
 * @throws \Laminas\Mail\Transport\Exception\RuntimeException
 */
public function mailHandler($to, $subject, $message, $headers, $parameters)
{
    set_error_handler([$this, 'handleMailErrors']);
    if ($parameters === null) {
        $result = mail($to, $subject, $message, $headers);
    } else {
        $result = mail($to, $subject, $message, $headers, $parameters);
    }
    restore_error_handler();

    if ($this->errstr !== null || ! $result) {
        $errstr = $this->errstr;
        if (empty($errstr)) {
            $errstr = 'Unknown error';
        }
        throw new Exception\RuntimeException('Unable to send mail: ' . $errstr);
    }
}

I'm suggesting you to dump all of the mail method parameter fields to see which one could create the problem and with that it might be easier to see what you missed in your observer (that you didn't provided).

2
  • I have added the code. Commented Jun 3, 2022 at 10:25
  • @saminakhan first try to send a simple mail without any attachments, to make sure it's not coming from your server configuration.
    – Claims
    Commented Jun 3, 2022 at 12:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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