Jump to content

Welcome, Guest!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

ssimard

Customer service - add predefined messages

Recommended Posts

Hi all,

well I went ahead and did it myself. Here are the steps in order to add predefined messages in the customer service section. I hope that it will be useful for someone.

** Sorry about the french screenshots and strings, I am running a french site so it is what it is :grinning: **

  1. You will have to duplicate the 2 database tables used by the orders predefined messages option. They are "xxordermessage" and "xxordermessagelang". Duplicate the structure only and rename them to "xxclientmessage" and "xxclientmessagelang".

In both new tables, change the first column name to "idclientmessage".

0_1522160311886_Capture d’écran 2018-03-27 à 10.13.40.png

0_1522160321902_Capture d’écran 2018-03-27 à 10.16.30.png

  1. In the override section, create AdminClientMessageController.php and add the following code. 0_1522160530797_Capture d’écran 2018-03-27 à 10.20.30.png

``` <?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * NOTICE OF LICENSE * * This source file is subject to the Open Software License (OSL 3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://opensource.org/licenses/osl-3.0.php * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@thirtybees.com so we can send you a copy immediately. * * DISCLAIMER * * Do not edit or add to this file if you wish to upgrade PrestaShop to newer * versions in the future. If you wish to customize PrestaShop for your * needs please refer to https://www.thirtybees.com for more information. * * @author Thirty Bees contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */

/** * Class AdminClientMessageControllerCore * * @since 1.0.0 */ class AdminClientMessageControllerCore extends AdminController { /** * AdminClintMessageControllerCore constructor. * * @since 1.0.0 */ public function __construct() { $this->bootstrap = true; $this->table = 'client_message'; $this->className = 'ClientMessage'; $this->lang = true;

    $this->addRowAction('edit');
    $this->addRowAction('delete');

    $this->context = Context::getContext();

    if (!Tools::getValue('realedit')) {
        $this->deleted = false;
    }

    $this->bulk_actions = [
        'delete' => [
            'text'    => $this->l('Delete selected'),
            'confirm' => $this->l('Delete selected items?'),
            'icon'    => 'icon-trash',
        ],
    ];

    $this->fields_list = [
        'id_client_message' => [
            'title' => $this->l('ID'),
            'align' => 'center',
        ],
        'name'             => [
            'title' => $this->l('Name'),
        ],
        'message'          => [
            'title'     => $this->l('Message'),
            'maxlength' => 300,
        ],
    ];

    $this->fields_form = [
        'legend' => [
            'title' => $this->l('Client messages'),
            'icon'  => 'icon-mail',
        ],
        'input'  => [
            [
                'type'     => 'text',
                'lang'     => true,
                'label'    => $this->l('Name'),
                'name'     => 'name',
                'size'     => 53,
                'required' => true,
            ],
            [
                'type'     => 'textarea',
                'lang'     => true,
                'label'    => $this->l('Message'),
                'name'     => 'message',
                'required' => true,
            ],
        ],
        'submit' => [
            'title' => $this->l('Save'),
        ],
    ];

    parent::__construct();
}

/**
 * Initialize page header toolbar
 *
 * @return void
 *
 * @since 1.0.0
 */
public function initPageHeaderToolbar()
{
    if (empty($this->display)) {
        $this->page_header_toolbar_btn['new_client_message'] = [
            'href' => static::$currentIndex.'&addclient_message&token='.$this->token,
            'desc' => $this->l('Ajouter un nouveau message client'),
            'icon' => 'process-icon-new',
        ];
    }

    parent::initPageHeaderToolbar();
}

} ``` 3. In override, create ClientMessage.php and paste the following code inside. 0_1522160734336_Capture d’écran 2018-03-27 à 10.24.38.png

```<?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * NOTICE OF LICENSE * * This source file is subject to the Open Software License (OSL 3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://opensource.org/licenses/osl-3.0.php * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@thirtybees.com so we can send you a copy immediately. * * DISCLAIMER * * Do not edit or add to this file if you wish to upgrade PrestaShop to newer * versions in the future. If you wish to customize PrestaShop for your * needs please refer to https://www.thirtybees.com for more information. * * @author Thirty Bees contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */

/** * Class ClientMessageCore * * @since 1.0.0 */ class ClientMessageCore extends ObjectModel { // @codingStandardsIgnoreStart /** @var string name name */ public $name;

/** @var string message content */
public $message;

/** @var string Object creation date */
public $date_add;
// @codingStandardsIgnoreEnd

/**
 * @see ObjectModel::$definition
 */
public static $definition = [
    'table'     => 'client_message',
    'primary'   => 'id_client_message',
    'multilang' => true,
    'fields'    => [
        'date_add' => ['type' => self::TYPE_DATE,                   'validate' => 'isDate'                                           ],
        'name'     => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128 ],
        'message'  => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isMessage',     'required' => true, 'size' => 1200],
    ],
];

protected $webserviceParameters = [
    'fields' => [
        'id'       => ['sqlId' => 'id_discount_type', 'xlink_resource' => 'client_message_lang'],
        'date_add' => ['sqlId' => 'date_add'],
    ],
];

/**
 * @param $idLang
 *
 * @return array|false|mysqli_result|null|PDOStatement|resource
 *
 * @since 1.0.0
 * @version 1.0.0 Initial version
 */
public static function getClientMessages($idLang)
{
    return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
    SELECT om.id_client_message, oml.name, oml.message
    FROM '._DB_PREFIX_.'client_message om
    LEFT JOIN '._DB_PREFIX_.'client_message_lang oml ON (oml.id_client_message = om.id_client_message)
    WHERE oml.id_lang = '.(int) $idLang.'
    ORDER BY name ASC');
}

}

```

  1. Next you will have to override AdminCustomerThreadsController.php in order to add the messages list to the template. Do it and add the following code into it. 0_1522161051645_Capture d’écran 2018-03-27 à 10.30.16.png

``` <?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * NOTICE OF LICENSE * * This source file is subject to the Open Software License (OSL 3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://opensource.org/licenses/osl-3.0.php * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@thirtybees.com so we can send you a copy immediately. * * DISCLAIMER * * Do not edit or add to this file if you wish to upgrade PrestaShop to newer * versions in the future. If you wish to customize PrestaShop for your * needs please refer to https://www.thirtybees.com for more information. * * @author Thirty Bees contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */

/** * Class AdminCustomerThreadsControllerCore * * @since 1.0.0 */ class AdminCustomerThreadsController extends AdminCustomerThreadsControllerCore { /** * Render view * * @return string * * @since 1.0.0 */ public function renderView() { if (!$idCustomerThread = (int) Tools::getValue('id_customer_thread')) { return ''; }

    $this->context = Context::getContext();
    if (!($thread = $this->loadObject())) {
        return '';
    }
    $this->context->cookie->{'customer_threadFilter_cl!id_contact'} = $thread->id_contact;

    $employees = Employee::getEmployees();

    $messages = CustomerThread::getMessageCustomerThreads($idCustomerThread);

    foreach ($messages as $key => $mess) {
        if ($mess['id_employee']) {
            $employee = new Employee($mess['id_employee']);
            $messages[$key]['employee_image'] = $employee->getImage();
        }
        if (isset($mess['file_name']) && $mess['file_name'] != '') {
            $messages[$key]['file_name'] = _THEME_PROD_PIC_DIR_.$mess['file_name'];
        } else {
            unset($messages[$key]['file_name']);
        }

        if ($mess['id_product']) {
            $product = new Product((int) $mess['id_product'], false, $this->context->language->id);
            if (Validate::isLoadedObject($product)) {
                $messages[$key]['product_name'] = $product->name;
                $messages[$key]['product_link'] = $this->context->link->getAdminLink('AdminProducts').'&updateproduct&id_product='.(int) $product->id;
            }
        }
    }

    $nextThread = CustomerThread::getNextThread((int) $thread->id);

    $contacts = Contact::getContacts($this->context->language->id);

    $actions = [];

    if ($nextThread) {
        $nextThread = [
            'href' => static::$currentIndex.'&id_customer_thread='.(int) $nextThread.'&viewcustomer_thread&token='.$this->token,
            'name' => $this->l('Reply to the next unanswered message in this thread'),
        ];
    }

    if ($thread->status != 'closed') {
        $actions['closed'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=2&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Mark as "handled"'),
            'name'  => 'setstatus',
            'value' => 2,
        ];
    } else {
        $actions['open'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Re-open'),
            'name'  => 'setstatus',
            'value' => 1,
        ];
    }

    if ($thread->status != 'pending1') {
        $actions['pending1'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=3&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Mark as "pending 1" (will be answered later)'),
            'name'  => 'setstatus',
            'value' => 3,
        ];
    } else {
        $actions['pending1'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Disable pending status'),
            'name'  => 'setstatus',
            'value' => 1,
        ];
    }

    if ($thread->status != 'pending2') {
        $actions['pending2'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=4&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Mark as "pending 2" (will be answered later)'),
            'name'  => 'setstatus',
            'value' => 4,
        ];
    } else {
        $actions['pending2'] = [
            'href'  => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
            'label' => $this->l('Disable pending status'),
            'name'  => 'setstatus',
            'value' => 1,
        ];
    }

    if ($thread->id_customer) {
        $customer = new Customer($thread->id_customer);
        $orders = Order::getCustomerOrders($customer->id);
        if ($orders && count($orders)) {
            $totalOk = 0;
            $ordersOk = [];
            foreach ($orders as $key => $order) {
                if ($order['valid']) {
                    $ordersOk[] = $order;
                    $totalOk += $order['total_paid_real'] / $order['conversion_rate'];
                }
                $orders[$key]['date_add'] = Tools::displayDate($order['date_add']);
                $orders[$key]['total_paid_real'] = Tools::displayPrice($order['total_paid_real'], new Currency((int) $order['id_currency']));
            }
        }

        $products = $customer->getBoughtProducts();
        if ($products && count($products)) {
            foreach ($products as $key => $product) {
                $products[$key]['date_add'] = Tools::displayDate($product['date_add'], null, true);
            }
        }
    }
    $timelineItems = $this->getTimeline($messages, $thread->id_order);
    $firstMessage = $messages[0];

    if (!$messages[0]['id_employee']) {
        unset($messages[0]);
    }

    $contact = '';
    foreach ($contacts as $c) {
        if ($c['id_contact'] == $thread->id_contact) {
            $contact = $c['name'];
        }
    }

    $this->tpl_view_vars = [
        'id_customer_thread'            => $idCustomerThread,
        'thread'                        => $thread,
        'actions'                       => $actions,
        'employees'                     => $employees,
        'current_employee'              => $this->context->employee,
        'messages'                      => $messages,
        'first_message'                 => $firstMessage,
        'contact'                       => $contact,
        'next_thread'                   => $nextThread,
        'orders'                        => isset($orders) ? $orders : false,
        'customer'                      => isset($customer) ? $customer : false,
        'products'                      => isset($products) ? $products : false,
        'total_ok'                      => isset($totalOk) ? Tools::displayPrice($totalOk, $this->context->currency) : false,
        'orders_ok'                     => isset($ordersOk) ? $ordersOk : false,
        'count_ok'                      => isset($ordersOk) ? count($ordersOk) : false,
        'PS_CUSTOMER_SERVICE_SIGNATURE' => str_replace('\r\n', "\n", Configuration::get('PS_CUSTOMER_SERVICE_SIGNATURE', (int) $thread->id_lang)),
        'timeline_items'                => $timelineItems,
        'clientMessages'                => ClientMessage::getClientMessages($thread->id_lang),
    ];

    if ($nextThread) {
        $this->tpl_view_vars['next_thread'] = $nextThread;
    }

    return AdminController::renderView();
}

} ``` 5. And finally, you will have to override the helper view.tpl with the following code. 0_1522161190650_Capture d’écran 2018-03-27 à 10.32.27.png

``` {* * 2007-2016 PrestaShop * * NOTICE OF LICENSE * * This source file is subject to the Academic Free License (AFL 3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://opensource.org/licenses/afl-3.0.php * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@prestashop.com so we can send you a copy immediately. * * DISCLAIMER * * Do not edit or add to this file if you wish to upgrade PrestaShop to newer * versions in the future. If you wish to customize PrestaShop for your * needs please refer to http://www.prestashop.com for more information. * * @author PrestaShop SA contact@prestashop.com * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA *}

{extends file="helpers/view/view.tpl"} {block name="overridetpl"} {include file="controllers/customerthreads/helpers/view/modal.tpl" }

{l s="Thread"}: #{$id_customer_thread|intval} {if isset($next_thread) && $next_thread} {$next_thread.name} {/if}
{foreach $actions as $action} {/foreach}
{if isset($customer->firstname)}

{$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} ({$customer->email|escape:'html':'UTF-8'})

{else}

{$thread->email|escape:'html':'UTF-8'}

{/if} {if isset($contact) && trim($contact) != ''} {l s="To:"} {$contact|escape:'html':'UTF-8'} {/if}
{if isset($customer->firstname)}

{if $count_ok} {l s='[1]%1$d[/1] order(s) validated for a total amount of [2]%2$s[/2]' sprintf=[$count_ok, $total_ok] tags=['', '']} {else} {l s="No orders validated for the moment"} {/if}

{l s="Customer since: %s" sprintf=[{dateFormat date=$customer->date_add full=0}]}

{/if}
{if !$first_message.id_employee} {include file="controllers/customer_threads/helpers/view/message.tpl" message=$first_message initial=true} {/if}
</div>
<div class="row">
    {foreach $messages as $message}
        {include file="controllers/customer_threads/helpers/view/message.tpl" message=$message initial=false}
    {/foreach}
</div>

{l s="Your answer to"} {if isset($customer->firstname)}{$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} {else} {$thread->email}{/if}

{if isset($current_employee->firstname)}{/if}

{l s='Configurer les messages prédéfinis'}

<div class="panel-footer">
    <!--
    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
        <i class="icon-magic icon-2x"></i><br>
        {l s="Choose a template"}
    </button>
    -->     
    <button class="btn btn-default pull-right" name="submitReply"><i class="process-icon-mail-reply"></i> {l s="Send"}</button>
    <input type="hidden" name="id_customer_thread" value="{$thread->id|intval}" />
    <input type="hidden" name="msg_email" value="{$thread->email}" />
</div>
</form>

{if count($timeline_items)}

{l s="Orders and messages timeline"}

{foreach $timeline_items as $dates} {foreach from=$dates key=date item=timeline_item} {include file="controllers/customer_threads/helpers/view/timeline_item.tpl" timeline_item=$timeline_item} {/foreach} {/foreach}

{/if}

{/block}

```

@@@@@ Code is complete, don't forget to delete class_index.php in your cache folder before running it.

Your customer service reply page should now look like this: 0_1522161552204_Capture d’écran 2018-03-27 à 10.36.01.png

To add the management link to the clients menu, just go to administration-menus and add an item like this: 0_1522161617283_Capture d’écran 2018-03-27 à 10.36.58.png

----> One last important thing, you may notice that the drop-down length is fine at first but does not scale if you resize your window. There is a bug in the chosen implementation (you will notice the same problem in the orders section). To fix it, you will have to edit js/admin.js and add the width parameter and set it to 100%. 0_1522161931317_Capture d’écran 2018-03-27 à 10.43.43.png

You should now be able to add as many predefined replies as you wish.

Hopefully I did not forget anything but feel free to post if you have problems and I will do my best to help you out.

Cheers !

Share this post


Link to post
Share on other sites

WOW!! If this is not against TB code standards and is not breaking anything, how about to include in the core?! how about PR on GitHub?

Share this post


Link to post
Share on other sites

Cool stuff! Maybe it's something for the core. But maybe the customer Service should be redesigned in a bigger way!? At least for me the customer service right now, is a big mess :)

Share this post


Link to post
Share on other sites

Yeah, our tech support staff wanted a few things added before using it.

I'll see what else is missing, we are also adding an option to simply send a message to multiple persons without reassigning it to anybody. Sometimes, our tech support staff are sending me and other colleagues questions that they don't know how to answer. They have to be able to send it to multiple persons at once and also, I don't want it to be 'reassigned' to me in the backend. So basically just a 'forward' option. We are almost done on that too.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...