My products have multiple options. I'd like the chosen options to be displayed in the order email I receive when an order is placed. Is this possible?

I see where email templates can be modified, but I don't see a code for what I'm asking for. Any help would be appreciated.

Thanks!
In General
Tuesday, June 04 2019, 06:27 PM
Share this post:
Responses (16)
  • Accepted Answer

    Friday, June 14 2019, 09:43 PM - #Permalink
    Hi Nick,

    Did you change the one liner or add the full code?

    In summary here are the solutions.

    The model file "order.php" (line 556):-

    /catalog/model/checkout/order.php

    $message = $this->emailtemplate->getMessage('OrderAll', 'order_' . (int)$order_status_id, $data);


    Change to :-

    $message = $this->emailtemplate->getDefaultOrderMessage('OrderAll', $data);


    That's it.

    or change it to this :-

    $this->load->language('mail/order');

    foreach ($data as $dataKey => $dataValue) {
    $$dataKey = $dataValue;
    }

    // HTML Mail
    $html_data = array();

    $html_data['title'] = sprintf($this->language->get('text_new_subject'), html_entity_decode($order_info['store_name'], ENT_QUOTES, 'UTF-8'), $order_info['order_id']);

    $html_data['text_greeting'] = sprintf($this->language->get('text_new_greeting'), html_entity_decode($order_info['store_name'], ENT_QUOTES, 'UTF-8'));
    $html_data['text_link'] = $this->language->get('text_new_link');
    $html_data['text_download'] = $this->language->get('text_new_download');
    $html_data['text_order_detail'] = $this->language->get('text_new_order_detail');
    $html_data['text_instruction'] = $this->language->get('text_new_instruction');
    $html_data['text_order_id'] = $this->language->get('text_new_order_id');
    $html_data['text_date_added'] = $this->language->get('text_new_date_added');
    $html_data['text_payment_method'] = $this->language->get('text_new_payment_method');
    $html_data['text_shipping_method'] = $this->language->get('text_new_shipping_method');
    $html_data['text_email'] = $this->language->get('text_new_email');
    $html_data['text_telephone'] = $this->language->get('text_new_telephone');
    $html_data['text_ip'] = $this->language->get('text_new_ip');
    $html_data['text_order_status'] = $this->language->get('text_new_order_status');
    $html_data['text_payment_address'] = $this->language->get('text_new_payment_address');
    $html_data['text_shipping_address'] = $this->language->get('text_new_shipping_address');
    $html_data['text_product'] = $this->language->get('text_new_product');
    $html_data['text_model'] = $this->language->get('text_new_model');
    $html_data['text_quantity'] = $this->language->get('text_new_quantity');
    $html_data['text_price'] = $this->language->get('text_new_price');
    $html_data['text_total'] = $this->language->get('text_new_total');
    $html_data['text_footer'] = $this->language->get('text_new_footer');

    $html_data['logo'] = $order_info['store_url'] . 'image/' . $this->config->get('config_logo');
    $html_data['store_name'] = $order_info['store_name'];
    $html_data['store_url'] = $order_info['store_url'];
    $html_data['customer_id'] = $order_info['customer_id'];
    $html_data['link'] = $order_info['store_url'] . 'index.php?route=account/order/info&order_id=' . $order_info['order_id'];

    if (isset($download_status) && $download_status) {
    $html_data['download'] = $order_info['store_url'] . 'index.php?route=account/download';
    } else {
    $html_data['download'] = '';
    }

    $html_data['order_id'] = $order_info['order_id'];
    $html_data['date_added'] = date($this->language->get('date_format_short'), strtotime($order_info['date_added']));
    $html_data['payment_method'] = $order_info['payment_method'];
    $html_data['shipping_method'] = $order_info['shipping_method'];
    $html_data['email'] = $order_info['email'];
    $html_data['telephone'] = $order_info['telephone'];
    $html_data['ip'] = $order_info['ip'];
    $html_data['order_status'] = $order_status;

    if ($comment && $notify) {
    $html_data['comment'] = nl2br($comment);
    } else {
    $html_data['comment'] = '';
    }

    if ($order_info['payment_address_format']) {
    $format = $order_info['payment_address_format'];
    } else {
    $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
    }

    $find = array(
    '{firstname}',
    '{lastname}',
    '{company}',
    '{address_1}',
    '{address_2}',
    '{city}',
    '{postcode}',
    '{zone}',
    '{zone_code}',
    '{country}'
    );

    $replace = array(
    'firstname' => $order_info['payment_firstname'],
    'lastname' => $order_info['payment_lastname'],
    'company' => $order_info['payment_company'],
    'address_1' => $order_info['payment_address_1'],
    'address_2' => $order_info['payment_address_2'],
    'city' => $order_info['payment_city'],
    'postcode' => $order_info['payment_postcode'],
    'zone' => $order_info['payment_zone'],
    'zone_code' => $order_info['payment_zone_code'],
    'country' => $order_info['payment_country']
    );

    $html_data['payment_address'] = str_replace(array("\r\n", "\r", "\n"), '', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), '', trim(str_replace($find, $replace, $format))));

    if ($order_info['shipping_address_format']) {
    $format = $order_info['shipping_address_format'];
    } else {
    $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
    }

    $find = array(
    '{firstname}',
    '{lastname}',
    '{company}',
    '{address_1}',
    '{address_2}',
    '{city}',
    '{postcode}',
    '{zone}',
    '{zone_code}',
    '{country}'
    );

    $replace = array(
    'firstname' => $order_info['shipping_firstname'],
    'lastname' => $order_info['shipping_lastname'],
    'company' => $order_info['shipping_company'],
    'address_1' => $order_info['shipping_address_1'],
    'address_2' => $order_info['shipping_address_2'],
    'city' => $order_info['shipping_city'],
    'postcode' => $order_info['shipping_postcode'],
    'zone' => $order_info['shipping_zone'],
    'zone_code' => $order_info['shipping_zone_code'],
    'country' => $order_info['shipping_country']
    );

    $html_data['shipping_address'] = str_replace(array("\r\n", "\r", "\n"), '', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), '', trim(str_replace($find, $replace, $format))));


    $this->load->model('tool/upload');

    // Products
    $html_data['products'] = array();

    $order_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_product WHERE order_id = '" . (int)$order_info['order_id'] . "'");

    foreach ($order_product_query->rows as $product) {
    $option_data = array();

    $order_option_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_option WHERE order_id = '" . (int)$order_info['order_id'] . "' AND order_product_id = '" . (int)$product['order_product_id'] . "'");

    foreach ($order_option_query->rows as $option) {
    if ($option['type'] != 'file') {
    $value = $option['value'];
    } else {
    $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);

    if ($upload_info) {
    $value = $upload_info['name'];
    } else {
    $value = '';
    }
    }

    $option_data[] = array(
    'name' => $option['name'],
    'value' => (utf8_strlen($value) > 20 ? utf8_substr($value, 0, 20) . '..' : $value)
    );
    }

    $html_data['products'][] = array(
    'name' => $product['name'],
    'model' => $product['model'],
    'option' => $option_data,
    'quantity' => $product['quantity'],
    'price' => $this->currency->format($product['price'] + ($this->config->get('config_tax') ? $product['tax'] : 0), $order_info['currency_code'], $order_info['currency_value']),
    'total' => $this->currency->format($product['total'] + ($this->config->get('config_tax') ? ($product['tax'] * $product['quantity']) : 0), $order_info['currency_code'], $order_info['currency_value'])
    );
    }

    // Vouchers
    $html_data['vouchers'] = array();


    $order_voucher_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_voucher WHERE order_id = '" . (int)$order_info['order_id'] . "'");

    foreach ($order_voucher_query->rows as $voucher) {
    $html_data['vouchers'][] = array(
    'description' => $voucher['description'],
    'amount' => $this->currency->format($voucher['amount'], $order_info['currency_code'], $order_info['currency_value']),
    );
    }

    // Order Totals
    $order_total_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_total` WHERE order_id = '" . (int)$order_info['order_id'] . "' ORDER BY sort_order ASC");

    foreach ($order_total_query->rows as $total) {
    $html_data['totals'][] = array(
    'title' => $total['title'],
    'text' => $this->currency->format($total['value'], $order_info['currency_code'], $order_info['currency_value']),
    );
    }

    if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/mail/order.tpl')) {
    $html = $this->load->view($this->config->get('config_template') . '/template/mail/order.tpl', $html_data);
    } else {
    $html = $this->load->view('default/template/mail/order.tpl', $html_data);
    }

    $message = $html;


    If you have made either of the above modifications then line 64 of the emailtemplate.php will become redundant, so whatever you change there will make no difference at all to your HTML email (it only has an effect if you leave "getMessage" in the "order.php" file.

    Aslo, the reason the check for the existence of the "order.tpl" actual template theme condition is false is because the path "DIR_TEMPLATE" is incorrect.

    Have a look here to see why:-

    /admin/define.php

    It uses a path to this "admin/view/template/your-chosen-theme/template/mail/order.tpl" when it should be "catalogue/view/theme/your-chosen-theme/template/mail/order.tpl

    You could perhaps use DIR_CATALOG instead or re-define DIR_TEMPLATE. Changing the existing one may fix a lot of problems, but cause others elsewhere. The references used in emailtemplate.php look incorrect, but without tracing it further before changing could cause other errors?


    This should probably work:-


    if (file_exists(DIR_CATALOG . '/view/theme/' . $this->config->get('config_template') . '/template/mail/order.tpl')) {
    $html = $this->load->view($this->config->get('config_template') . '/template/mail/order.tpl', $html_data);
    } else {
    $html = $this->load->view('default/template/mail/order.tpl', $html_data);
    }


    Regards,

    Hackasacka
    Like
    1
    The reply is currently minimized Show
  • Accepted Answer

    Tuesday, June 04 2019, 11:18 PM - #Permalink
    Hi Nick,


    The "demo" installation of Arastta v 1.6.2 has examples of products with options setup ready to test i.e. shoes/canvas-daily.

    You haven't explained where you have found the order message email template, so we do not know which template you are using or if it is a custom design? Also, letting the forum know what you already understand may be useful in the resolution.

    We think that when purchases are confirmed from the "Checkout" page (which calls on a few modules in the background), the shopper is re-directed to the "Success" page. At this point the order details are now already stored in the backend database for "Orders", so this is queried to get the data (free checkout was enabled so no payment credit card details were needed).

    It can be seen from the controller for orders does check if there are any product options.

    The controller file "order.php" (line 482 - 497)

    /catalog/controller/account/order.php

                    if ($product_info) {
    $option_data = array();

    $order_options = $this->model_account_order->getOrderOptions($order_product_info['order_id'], $order_product_id);

    foreach ($order_options as $order_option) {
    if ($order_option['type'] == 'select' || $order_option['type'] == 'radio' || $order_option['type'] == 'image') {
    $option_data[$order_option['product_option_id']] = $order_option['product_option_value_id'];
    } elseif ($order_option['type'] == 'checkbox') {
    $option_data[$order_option['product_option_id']][] = $order_option['product_option_value_id'];
    } elseif ($order_option['type'] == 'text' || $order_option['type'] == 'textarea' || $order_option['type'] == 'date' || $order_option['type'] == 'datetime' || $order_option['type'] == 'time') {
    $option_data[$order_option['product_option_id']] = $order_option['value'];
    } elseif ($order_option['type'] == 'file') {
    $option_data[$order_option['product_option_id']] = $this->encryption->encrypt($order_option['value']);
    }
    }


    The model for "success" returns the information as the $message variable at the end of the function.


    The the model file "success.php" (whole file):-

    /catalog/model/checkout/success.php

    <?php
    /**
    * @package Arastta eCommerce
    * @copyright 2015-2017 Arastta Association. All rights reserved.
    * @copyright See CREDITS.txt for credits and other copyright notices.
    * @license GNU GPL version 3; see LICENSE.txt
    * @link https://arastta.org
    */

    class ModelCheckoutSuccess extends Model
    {
    public function getMessage($order_id)
    {
    $message = '';
    $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_history WHERE order_id = '" . (int)$order_id . "'");

    if (!empty($query->num_rows)) {
    $order_status_id = $query->row['order_status_id'];

    $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_status WHERE order_status_id = '" . (int)$order_status_id . "' AND language_id ='" . $this->config->get('config_language_id') ."'") ;

    if (!empty($query->num_rows)) {
    $message = $query->row['message'];
    }
    }

    return $message;
    }
    }


    The confirmation email to both the store owner and the buyer is constructed as an "HTML" file which does include the product options.

    /catalog/view/theme/second/template/mail/order.tpl (lines 82- 94)

            <?php foreach ($products as $product) { ?>
    <tr>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['name']; ?>
    <?php foreach ($product['option'] as $option) { ?>

    <small> - <?php echo $option['name']; ?>: <?php echo $option['value']; ?></small>
    <?php } ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['model']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['quantity']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['price']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['total']; ?></td>
    </tr>
    <?php } ?>


    It should be listed in the HTML small tag with a break "br" after each entry, but it doesn't become visible unless the email is viewed as plain text and there does not appear to be a style hiding it from view? It will probably become obvious later? Try using your browser inspector to locate the code and you could update the email template to be HTML5 too.

    Perhaps if you change the above code you could ensure it is more visible in HTML for test purposes and then work backwards?


    This page is then sent by the model for "order".


    The the model file "order.php" (lines 585 - 607):-

    /catalog/model/checkout/order.php

                    if ($this->config->get('config_order_mail')) {

    if (!isset($mail)) {
    $mail = new Mail($this->config->get('config_mail'));
    $mail->setFrom($this->config->get('config_email'));
    $mail->setSender($order_info['store_name']);
    }

    $mail->setSubject($subject);
    $mail->setHTML($message);
    $mail->setText($text);
    $mail->setTo($this->config->get('config_email'));
    $mail->send();

    $emails = explode(',', $this->config->get('config_alert_emails'));

    foreach ($emails as $email) {
    if ($email && preg_match('/^[^\@]+@.*\.[a-z]{2,6}$/i', $email)) {
    $mail->setTo($email);
    $mail->send();
    }
    }
    }


    The information here will hopefully help you identify where the data is coming from and where it can be modified.

    In summary it already does work, but you will have to re-design the email template to suit your needs.


    Regards,


    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Wednesday, June 05 2019, 04:15 PM - #Permalink
    Thank you Hackasacka for your detailed response. I've attached images of the order confirmation screen and the email to illustrate my issue. I believe the email is generated using the "Processing" email template. I have not modified this template from the standard. Upon inspection of the "Short Code" options, I don't recognize which I would need to add to the template to get the options details, or if it's even an option.

    I've inspected the code of the email, it does not have the details information, so it's not simply not displaying. Here is the code:

    <tr>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: left; padding: 7px;"><img src=3D"http://b=
    rechbuhler.kbprint.us/image/cache/catalog/brechbuhler/bc-bs-80x80.jpg" styl=
    e=3D"width: 50px;height: 50px;padding: auto;" /></td>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: left; padding: 7px;">
    <p>Business Cards - Brechbuhler Scales</p>
    </td>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: left; padding: 7px;">bc-scales</td>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: right; padding: 7px;">1</td>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: right; padding: 7px;">$101.65</td>
    <td style=3D"font-size: 12px; border-right: 1px solid #DDDDDD; border-botto=
    m: 1px solid #DDDDDD; text-align: right; padding: 7px;">$101.65</td>
    </tr>


    I did notice the code you posted was from the "Second" template. I'm using the default template. Maybe the default template doesn't have this functionality? What would be the easiest way to add it? Should I attempt to vqmod the lines you highlighted into the default order.tpl?

    Thanks
    The reply is currently minimized Show
  • Accepted Answer

    Wednesday, June 05 2019, 04:29 PM - #Permalink
    UPDATE: I tried switching the template to "Second" but the email received was the same as prior, with no order details.
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 07 2019, 01:43 AM - #Permalink
    Hi Nick,

    Both templates use the same model file to process the data being sent in the emails to the owner and buyer.

    The the model file "order.php" (lines 555 - 607):-

    /catalog/model/checkout/order.php

                    $subject = $this->emailtemplate->getSubject('OrderAll', 'order_' . (int)$order_status_id, $data);
    $message = $this->emailtemplate->getMessage('OrderAll', 'order_' . (int)$order_status_id, $data);

    $getTotal = $order_total->rows;

    $textData = array(
    'order_info' => $order_info,
    'order_id' => $order_id,
    'order_status' => $order_status,
    'comment' => $comment,
    'notify' => $notify,
    'getProducts' => $this->getOrderProducts($order_id),
    'getVouchers' => $this->getOrderVouchers($order_id),
    'getTotal' => $getTotal
    );

    $text = $this->emailtemplate->getText('Order', 'order',$textData);

    #Send Email
    if ((!$order_info['order_status_id'] && $order_status_id) || ($order_info['order_status_id'] && $order_status_id && $notify)) {
    $mail = new Mail($this->config->get('config_mail'));
    $mail->setTo($order_info['email']);
    $mail->setFrom($this->config->get('config_email'));
    $mail->setSender($order_info['store_name']);
    $mail->setSubject($subject);
    $mail->setHtml($text);
    $mail->setText($text);
    $mail->send();
    }

    if ($this->config->get('config_order_mail')) {

    if (!isset($mail)) {
    $mail = new Mail($this->config->get('config_mail'));
    $mail->setFrom($this->config->get('config_email'));
    $mail->setSender($order_info['store_name']);
    }

    $mail->setSubject($subject);
    $mail->setHTML($message);
    $mail->setText($text);
    $mail->setTo($this->config->get('config_email'));
    $mail->send();

    $emails = explode(',', $this->config->get('config_alert_emails'));

    foreach ($emails as $email) {
    if ($email && preg_match('/^[^\@]+@.*\.[a-z]{2,6}$/i', $email)) {
    $mail->setTo($email);
    $mail->send();
    }
    }
    }


    Note that line 580 has been modified from
                        $mail->setHTML($message);


    This sends the body of the email message (including product options )to the buyers email address as fairly untidy plain text, but shows it works without the need to change the existing email tpl's.

    If you also modify line 594 to :-

                        $mail->setText($text);


    The store owner will also receive all of the order options including the product options as a lines of text.

    Arastta is using library functions to get the data from the email template, but obviously you could re-arrange this as you like to a different format.

    The code for this text is here:-

                    $textData = array(
    'order_info' => $order_info,
    'order_id' => $order_id,
    'order_status' => $order_status,
    'comment' => $comment,
    'notify' => $notify,
    'getProducts' => $this->getOrderProducts($order_id),
    'getVouchers' => $this->getOrderVouchers($order_id),
    'getTotal' => $getTotal
    );

    $text = $this->emailtemplate->getText('Order', 'order',$textData);


    For the store owners or the buyer to see the product options selected all you need to do is view the email as plain text, so try another web email browser or email client to see it already exists.

    Anyway, if you do not need it to look pretty the above option for the store owner's email will suffice.


    Regards,


    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 07 2019, 09:55 PM - #Permalink
    Thanks again Hackasacka. I replaced line 594 in order.php and it indeed sent the email as plaintext and it included the product options. Unfortunately it was just an unwieldy wall of text that is too messy do deal with for daily business operation.

    So the next questions is how do we add the product options formatted in the HTML email? You posted above that order.tpl should send the details marked-up with the small tags, but I'm not getting that markup in the HTML emails. So either order.tpl isnt getting used, or there is a bug in the code somewhere.

    I did a test order using the demo site to see if it's just my installation, but I have yet to get a confirmation email.

    Thanks for your continued help.
    The reply is currently minimized Show
  • Accepted Answer

    Saturday, June 08 2019, 12:10 AM - #Permalink
    Hi Nick,

    We should of looked at the Arastta documentation:-

    https://arastta.org/docs/user-manual/system/email-templates

    It's on page 2 :-

    "Complete Order status.complete"

    However, there isn't an obvious variable for the product options, we may have to add it ourselves?

    Possibly this is where it needs to be corrected:-

    /system/library/emailtemplate.php

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Monday, June 10 2019, 03:25 PM - #Permalink
    Happy Monday,
    I don't see a second page to your link. I believe that "Complete Order status.complete" is referring to the order status, such as pending, processing, complete, etc. The email templates use short codes, but I don't see one for product options. I imagine it can be added, but my PHP isn't good enough to reverse engineer it. I was hoping someone on these boards could help me.
    The reply is currently minimized Show
  • Accepted Answer

    Monday, June 10 2019, 03:59 PM - #Permalink
    Hi Nick,

    You're right there isn't a short code, so we need to add the code ourselves. Just not worked it out yet. We are viewing the right files to update, and the email template does already cater for product options being looped through where the small tag is, but it isn't being passed the data to process yet.

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Tuesday, June 11 2019, 11:50 PM - #Permalink
    Hi Nick,

    It appears that the Arastta team use the "Email Templates" with short codes as a default (if they exist). This of course allows Admin users without programming knowledge and only a only a little HTML to modify the emailed messages.

    The HTML and data from this "Complete Order status.complete" is combined with the template using variable message inside the body and the whole content is returned to send as the HTML data for setHtml in the model for the order email :-

    /catalog/view/theme/second/template/mail/default.tpl

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd">
    <html>
    <head>
    <m-eta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <l-ink type="text/css" href="/<?php echo $site_url; ?>view/stylesheet/stylesheet.css" rel="stylesheet" media="screen" />
    <l-ink href="/<?php echo $site_url; ?>view/j-avascript/bootstrap/arastta/arastta.css" type="text/css" rel="stylesheet" />
    <s-cript type="text/j-avascript" src="/<?php echo $site_url; ?>view/j-avascript/jquery/jquery-2.1.1.min.js"></script>
    <s-cript type="text/j-avascript" src="/<?php echo $site_url; ?>view/j-avascript/bootstrap/js/bootstrap.min.js"></script>
    <t-itle><?php echo $title; ?></title>
    </head>
    <body style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: #000000;">
    <?php echo $message; ?>
    </body>
    </html>


    It has a shortfall regarding the product options being detailed (probably because it's more complicated and would require PHP to loop through the options?).

    However, Arastta have preserved the ability to use the original email template for orders (with the small tag for product options as discussed earlier).

    /catalog/view/theme/your_theme/template/mail/order.tpl

    The model file "order.php" (line 556):-

    /catalog/model/checkout/order.php

    $message = $this->emailtemplate->getMessage('OrderAll', 'order_' . (int)$order_status_id, $data);


    Change to :-

    $message = $this->emailtemplate->getDefaultOrderMessage('OrderAll', $data);


    That's it.

    If you are interested it is worth looking at the "getMessage" and "getDefaultOrderMessage" public functions. They are in the library :-

    The library file "emailtemplate.php" (line 64):-

    system/library/emailtemplate.php

    public function getMessage($type, $template_id, $data)


    Also, have a look at (line 1162)

    public function getDefaultOrderMessage($type_id, $data)


    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Wednesday, June 12 2019, 10:18 PM - #Permalink
    Hi Nick,

    There's a bug in the public function "emailtemplate.php" whereby the path for the "logo" is the site admin url instead of the store url.

    The library file "emailtemplate.php" (line 1197):-

    system/library/emailtemplate.php

    $html_data['logo'] = $this->config->get('config_url') . 'image/' . $this->config->get('config_logo');


    Change to :-

    $html_data['logo'] = $order_info['store_url'] . 'image/' . $this->config->get('config_logo');


    Other than this it should now mean you have all the order details including the options and can style and add details to make it customised.

    We also noticed that although the template chosen for our testing is the "Second" theme and it does have an "order.tpl" in the correct directory the "Default" templates "order.tpl" is still being used?

    As they are both currently the same it may be wise to modify them both or change the code in the "emailtemplate" to meet your requirements.

    You can probably see the logic in the conditional statement below.

    The library file "emailtemplate.php" (line 1357 - 1363):-

    system/library/emailtemplate.php

    if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/mail/order.tpl')) {
    $html = $this->load->view($this->config->get('config_template') . '/template/mail/order.tpl', $html_data);
    } else {
    $html = $this->load->view('default/template/mail/order.tpl', $html_data);
    }

    return $html;



    Hope this helps.

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Thursday, June 13 2019, 08:48 PM - #Permalink
    I'm not sure what emailtemplate.php is being used for. I checked the emails I've been receiving for orders, and the logo linked correctly to the storefront. I made your suggested update, did another test order, and the logo had the same link it did prior.

    Starting on line 1295 though, there is this code:

    // Products
    $html_data['products'] = array();

    $order_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_product WHERE order_id = '" . (int)$order_info['order_id'] . "'");

    foreach ($order_product_query->rows as $product) {
    $option_data = array();

    $order_option_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_option WHERE order_id = '" . (int)$order_info['order_id'] . "' AND order_product_id = '" . (int)$product['order_product_id'] . "'");

    foreach ($order_option_query->rows as $option) {
    if ($option['type'] != 'file') {
    $value = $option['value'];
    } else {
    $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);

    if ($upload_info) {
    $value = $upload_info['name'];
    } else {
    $value = '';
    }
    }

    $option_data[] = array(
    'name' => $option['name'],
    'value' => (utf8_strlen($value) > 20 ? utf8_substr($value, 0, 20) . '..' : $value)
    );
    }

    $html_data['products'][] = array(
    'name' => $product['name'],
    'model' => $product['model'],
    'option' => $option_data,
    'quantity' => $product['quantity'],
    'price' => $this->currency->format($product['price'] + ($this->config->get('config_tax') ? $product['tax'] : 0), $order_info['currency_code'], $order_info['currency_value']),
    'total' => $this->currency->format($product['total'] + ($this->config->get('config_tax') ? ($product['tax'] * $product['quantity']) : 0), $order_info['currency_code'], $order_info['currency_value'])
    );
    }


    This looks like how the product and options are being stored. Then looking at order.tpl I see this code:


    <?php foreach ($products as $product) { ?>
    <tr>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['name']; ?>
    <?php foreach ($product['option'] as $option) { ?>

    <small> - <?php echo $option['name']; ?>: <?php echo $option['value']; ?></small>
    <?php } ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['model']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['quantity']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['price']; ?></td>
    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: right; padding: 7px;"><?php echo $product['total']; ?></td>
    </tr>
    <?php } ?>


    Which looks to me to be the functionality I'm looking for, it's just not working. Maybe the $option data (array?) isn't being passed properly?
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 14 2019, 12:39 AM - #Permalink
    Hi Nick,

    In the first place you have to make the modifications to the "order" model to use a different public function as mentioned earlier in the thread. This will then pass the data for the product options in the array. The original code calls a different function in the emailtemplate file which does not pass the array or even use the order.tpl template.

    This is where the emailtemplate comes from :-

    $this->emailtemplate->some-function-that-exits-in-the-file


    That calls a function from the library.

    The model file "order.php" (line 556):-

    /catalog/model/checkout/order.php

    $message = $this->emailtemplate->getDefaultOrderMessage('OrderAll', $data);



    Also, as there are two order.tpl files for the two demo themes place some unigue code in them to confirm which is being used?

    /catalog/view/theme/default/template/mail/order.tpl

    /catalog/view/theme/your_theme/template/mail/order.tpl

    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['model']; ?><h1>template x</td>


    And for example :-

    <td style="font-size: 12px; border-right: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD; text-align: left; padding: 7px;"><?php echo $product['model']; ?><h1>template y</td>


    If you read the lines of code above line 1295 in the emailtemplate.php you will see the function we are changing in the order.php model, but it won't be called if you do not change it first and you will get the same result (it will use the Admin email shortcode template with the logo working).

    Let us know if it works?

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 14 2019, 01:01 AM - #Permalink
    Hi Nick,

    Another possible solution might be to create your own unique shortcode for the template you can edit in Admin.

    If you could modify the function in the emailtemplate file (line 64):-

        // Mail Message
    public function getMessage($type, $template_id, $data)
    {
    $template = $this->getEmailTemplate($template_id);

    $findFunctionName = 'get' . ucwords($type) . 'Find';
    $replaceFunctionName = 'get' . ucwords($type) . 'Replace';

    $find = array();

    if (method_exists($this, $findFunctionName)) {
    $find = $this->$findFunctionName();
    }

    $this->trigger->fire('post.emailtemplate.message.shortcode', array(&$find));

    $replace = array();

    if (method_exists($this, $replaceFunctionName)) {
    $replace = $this->$replaceFunctionName($data);
    }

    $this->trigger->fire('post.emailtemplate.message.replace', array(&$replace, &$data));

    if (!empty($template['description'])) {
    if (ucwords($type) == 'OrderAll') {
    preg_match('/{product:start}(.*){product:stop}/Uis', $template['description'], $template_product);
    if (!empty($template_product[1])) {
    $template['description'] = str_replace($template_product[1], '', $template['description']);
    }

    preg_match('/{voucher:start}(.*){voucher:stop}/Uis', $template['description'], $template_voucher);
    if (!empty($template_voucher[1])) {
    $template['description'] = str_replace($template_voucher[1], '', $template['description']);
    }

    preg_match('/{comment:start}(.*){comment:stop}/Uis', $template['description'], $template_comment);
    if (!empty($template_comment[1])) {
    $template['description'] = str_replace($template_comment[1], '', $template['description']);
    }

    preg_match('/{tax:start}(.*){tax:stop}/Uis', $template['description'], $template_tax);
    if (!empty($template_tax[1])) {
    $template['description'] = str_replace($template_tax[1], '', $template['description']);
    }

    preg_match('/{total:start}(.*){total:stop}/Uis', $template['description'], $template_total);
    if (!empty($template_total[1])) {
    $template['description'] = str_replace($template_total[1], '', $template['description']);
    }
    }

    $message = trim(str_replace($find, $replace, $template['description']));
    } else {
    $message = $this->getDefaultMessage($type, $template_id, $data);
    }

    $this->trigger->fire('post.emailtemplate.message.message', array(&$message));

    $data['title'] = $this->getSubject($type, $template_id, $data);
    $data['message'] = $message;
    $data['site_url'] = ($this->request->server['HTTPS']) ? HTTPS_SERVER : HTTP_SERVER;

    if (Client::isCatalog()) {
    if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/mail/default.tpl')) {
    $message = $this->load->view($this->config->get('config_template') . '/template/mail/default.tpl', $data);
    } else {
    $message = $this->load->view('default/template/mail/default.tpl', $data);
    }
    } else {
    $message = $this->load->view('mail/default.tpl', $data);
    }

    return $message;
    }


    Add some code here to loop through the product options:-


    preg_match('/{product:start}(.*){product:stop}/Uis', $template['description'], $template_product);
    if (!empty($template_product[1])) {
    $template['description'] = str_replace($template_product[1], '', $template['description']);
    }


    Your shortcode would need to be substituted for the statement, and then the loop adding :-


    preg_match('/{optionproduct:start}(.*){optionproduct:stop}/Uis', $template['description'], $template_product);


    <?php foreach ($product['option'] as $option) { ?>


    Then add the shortcode to the "Email Template" via Arastta Admin backend to "order complete"

    {optionproduct:start} {optionproduct:stop}

    This would be better to try after you try the first solution as it's adding complications and this hasn't been tested yet.

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 14 2019, 03:04 PM - #Permalink
    Hi Nick,

    This may help you get the result you expect.

    Modify the order model so that all of the processing of data exists there instead of in the emailtemplate.php so it is easier to understand (the functions are only useful to save extra lines of code if they are called from several different places making it modular). This should be slightly faster. You could even compile the html here to by changing a few lines of code after pasting the order.tpl into the order.php file.

    Remove the line of code with the statement below.

    The model file "order.php" (line 556):-

    /catalog/model/checkout/order.php

    $message = $this->emailtemplate->getMessage('OrderAll', 'order_' . (int)$order_status_id, $data);


    Replace with :-

    $this->load->language('mail/order');

    foreach ($data as $dataKey => $dataValue) {
    $$dataKey = $dataValue;
    }

    // HTML Mail
    $html_data = array();

    $html_data['title'] = sprintf($this->language->get('text_new_subject'), html_entity_decode($order_info['store_name'], ENT_QUOTES, 'UTF-8'), $order_info['order_id']);

    $html_data['text_greeting'] = sprintf($this->language->get('text_new_greeting'), html_entity_decode($order_info['store_name'], ENT_QUOTES, 'UTF-8'));
    $html_data['text_link'] = $this->language->get('text_new_link');
    $html_data['text_download'] = $this->language->get('text_new_download');
    $html_data['text_order_detail'] = $this->language->get('text_new_order_detail');
    $html_data['text_instruction'] = $this->language->get('text_new_instruction');
    $html_data['text_order_id'] = $this->language->get('text_new_order_id');
    $html_data['text_date_added'] = $this->language->get('text_new_date_added');
    $html_data['text_payment_method'] = $this->language->get('text_new_payment_method');
    $html_data['text_shipping_method'] = $this->language->get('text_new_shipping_method');
    $html_data['text_email'] = $this->language->get('text_new_email');
    $html_data['text_telephone'] = $this->language->get('text_new_telephone');
    $html_data['text_ip'] = $this->language->get('text_new_ip');
    $html_data['text_order_status'] = $this->language->get('text_new_order_status');
    $html_data['text_payment_address'] = $this->language->get('text_new_payment_address');
    $html_data['text_shipping_address'] = $this->language->get('text_new_shipping_address');
    $html_data['text_product'] = $this->language->get('text_new_product');
    $html_data['text_model'] = $this->language->get('text_new_model');
    $html_data['text_quantity'] = $this->language->get('text_new_quantity');
    $html_data['text_price'] = $this->language->get('text_new_price');
    $html_data['text_total'] = $this->language->get('text_new_total');
    $html_data['text_footer'] = $this->language->get('text_new_footer');

    $html_data['logo'] = $order_info['store_url'] . 'image/' . $this->config->get('config_logo');
    $html_data['store_name'] = $order_info['store_name'];
    $html_data['store_url'] = $order_info['store_url'];
    $html_data['customer_id'] = $order_info['customer_id'];
    $html_data['link'] = $order_info['store_url'] . 'index.php?route=account/order/info&order_id=' . $order_info['order_id'];

    if (isset($download_status) && $download_status) {
    $html_data['download'] = $order_info['store_url'] . 'index.php?route=account/download';
    } else {
    $html_data['download'] = '';
    }

    $html_data['order_id'] = $order_info['order_id'];
    $html_data['date_added'] = date($this->language->get('date_format_short'), strtotime($order_info['date_added']));
    $html_data['payment_method'] = $order_info['payment_method'];
    $html_data['shipping_method'] = $order_info['shipping_method'];
    $html_data['email'] = $order_info['email'];
    $html_data['telephone'] = $order_info['telephone'];
    $html_data['ip'] = $order_info['ip'];
    $html_data['order_status'] = $order_status;

    if ($comment && $notify) {
    $html_data['comment'] = nl2br($comment);
    } else {
    $html_data['comment'] = '';
    }

    if ($order_info['payment_address_format']) {
    $format = $order_info['payment_address_format'];
    } else {
    $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
    }

    $find = array(
    '{firstname}',
    '{lastname}',
    '{company}',
    '{address_1}',
    '{address_2}',
    '{city}',
    '{postcode}',
    '{zone}',
    '{zone_code}',
    '{country}'
    );

    $replace = array(
    'firstname' => $order_info['payment_firstname'],
    'lastname' => $order_info['payment_lastname'],
    'company' => $order_info['payment_company'],
    'address_1' => $order_info['payment_address_1'],
    'address_2' => $order_info['payment_address_2'],
    'city' => $order_info['payment_city'],
    'postcode' => $order_info['payment_postcode'],
    'zone' => $order_info['payment_zone'],
    'zone_code' => $order_info['payment_zone_code'],
    'country' => $order_info['payment_country']
    );

    $html_data['payment_address'] = str_replace(array("\r\n", "\r", "\n"), '', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), '', trim(str_replace($find, $replace, $format))));

    if ($order_info['shipping_address_format']) {
    $format = $order_info['shipping_address_format'];
    } else {
    $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
    }

    $find = array(
    '{firstname}',
    '{lastname}',
    '{company}',
    '{address_1}',
    '{address_2}',
    '{city}',
    '{postcode}',
    '{zone}',
    '{zone_code}',
    '{country}'
    );

    $replace = array(
    'firstname' => $order_info['shipping_firstname'],
    'lastname' => $order_info['shipping_lastname'],
    'company' => $order_info['shipping_company'],
    'address_1' => $order_info['shipping_address_1'],
    'address_2' => $order_info['shipping_address_2'],
    'city' => $order_info['shipping_city'],
    'postcode' => $order_info['shipping_postcode'],
    'zone' => $order_info['shipping_zone'],
    'zone_code' => $order_info['shipping_zone_code'],
    'country' => $order_info['shipping_country']
    );

    $html_data['shipping_address'] = str_replace(array("\r\n", "\r", "\n"), '', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), '', trim(str_replace($find, $replace, $format))));


    $this->load->model('tool/upload');

    // Products
    $html_data['products'] = array();

    $order_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_product WHERE order_id = '" . (int)$order_info['order_id'] . "'");

    foreach ($order_product_query->rows as $product) {
    $option_data = array();

    $order_option_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_option WHERE order_id = '" . (int)$order_info['order_id'] . "' AND order_product_id = '" . (int)$product['order_product_id'] . "'");

    foreach ($order_option_query->rows as $option) {
    if ($option['type'] != 'file') {
    $value = $option['value'];
    } else {
    $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);

    if ($upload_info) {
    $value = $upload_info['name'];
    } else {
    $value = '';
    }
    }

    $option_data[] = array(
    'name' => $option['name'],
    'value' => (utf8_strlen($value) > 20 ? utf8_substr($value, 0, 20) . '..' : $value)
    );
    }

    $html_data['products'][] = array(
    'name' => $product['name'],
    'model' => $product['model'],
    'option' => $option_data,
    'quantity' => $product['quantity'],
    'price' => $this->currency->format($product['price'] + ($this->config->get('config_tax') ? $product['tax'] : 0), $order_info['currency_code'], $order_info['currency_value']),
    'total' => $this->currency->format($product['total'] + ($this->config->get('config_tax') ? ($product['tax'] * $product['quantity']) : 0), $order_info['currency_code'], $order_info['currency_value'])
    );
    }

    // Vouchers
    $html_data['vouchers'] = array();


    $order_voucher_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_voucher WHERE order_id = '" . (int)$order_info['order_id'] . "'");

    foreach ($order_voucher_query->rows as $voucher) {
    $html_data['vouchers'][] = array(
    'description' => $voucher['description'],
    'amount' => $this->currency->format($voucher['amount'], $order_info['currency_code'], $order_info['currency_value']),
    );
    }

    // Order Totals
    $order_total_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_total` WHERE order_id = '" . (int)$order_info['order_id'] . "' ORDER BY sort_order ASC");

    foreach ($order_total_query->rows as $total) {
    $html_data['totals'][] = array(
    'title' => $total['title'],
    'text' => $this->currency->format($total['value'], $order_info['currency_code'], $order_info['currency_value']),
    );
    }

    if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/mail/order.tpl')) {
    $html = $this->load->view($this->config->get('config_template') . '/template/mail/order.tpl', $html_data);
    } else {
    $html = $this->load->view('default/template/mail/order.tpl', $html_data);
    }

    $message = $html;


    Our initial mistake was to make some assumptions without testing things thoroughly. For instance all of the order emails look very similar even if they are actually from different sources. Therefore you could add something unique to the order.tpl template so it is distinguishable from the others..

    catalog/view/theme/default/template/mail/order.tpl ( line 9):-


    <p style="margin-top: 0px; margin-bottom: 20px;"><?php echo $text_greeting; ?></p>
    <h1>This is the order template</h1>


    It may be worth mentioning that emails are viewed both as text and HTML depending on the recipients preferences, but plain text is significantly more secure. Often HTML is not allowed due to it connecting to external sources or there is a tool bar option to view the external links.

    It is therefore good practice to cater for all scenarios when setting up your email formats. The email clients view them differently too.

    https://group-mail.com/html-email/tips-to-create-html-email-that-works-with-all-email-clients-part-1/

    It is frustrating to view an email as plain text and then have to sift through all the styling and HTML just to find a few lines of insignificant information.

    Did you test any other email clients to see the text part of the message has always included the product options yet? If you have access to "Cpanel" there maybe email clients "Horde" and "Roundcube" available which illustrate this point.

    Roundcube is HTML biased and Horde is not by default. If the email is well formatted then "Horde" would give the option to view an HTML version too.

    Hope this helps.

    Regards,

    Hackasacka
    The reply is currently minimized Show
  • Accepted Answer

    Friday, June 14 2019, 04:43 PM - #Permalink
    Success!

    Modifying /catalog/model/checkout/order.php at line 556 did it! I also modified emailtemplate.php (line 64), was this also necessary or was this an extra step? I must have missed that step when I was reading through before.

    My php skill is a bit too low for most of this thread so I apologize, but thanks for bearing with me. Thank you for your help!
    The reply is currently minimized Show
Your Reply