Updated the flow of customer message and customer service

This commit is contained in:
vishalsingh.qlo325
2025-02-24 11:08:44 +05:30
parent b13ef0a185
commit b4f03b3917
25 changed files with 553 additions and 289 deletions

View File

@@ -51,10 +51,22 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
$icon_array = array(
'open' => array('class' => 'icon-circle text-success', 'alt' => $this->l('Open')),
'closed' => array('class' => 'icon-circle text-danger', 'alt' => $this->l('Closed')),
'pending1' => array('class' => 'icon-circle text-warning', 'alt' => $this->l('Pending 1')),
'pending2' => array('class' => 'icon-circle text-warning', 'alt' => $this->l('Pending 2')),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN => array(
'class' => 'icon-circle text-success',
'alt' => $this->l('Open')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED => array(
'class' => 'icon-circle text-danger',
'alt' => $this->l('Closed')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1 => array(
'class' => 'icon-circle text-warning',
'alt' => $this->l('Pending 1')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2 => array(
'class' => 'icon-circle text-warning',
'alt' => $this->l('Pending 2')
),
);
$status_array = array();
@@ -67,7 +79,9 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->access_join = ' LEFT JOIN '._DB_PREFIX_.'orders ord ON (a.id_order = ord.id_order)';
$this->access_join .= ' LEFT JOIN '._DB_PREFIX_.'htl_booking_detail hbd ON (hbd.id_order = ord.id_order)';
if ($acsHtls = HotelBranchInformation::getProfileAccessedHotels($this->context->employee->id_profile, 1, 1)) {
$this->access_where = ' AND IF(a.`id_order`, hbd.`id_hotel` IN ('.implode(',', $acsHtls).'), 1)';
$this->access_where = ' WHERE IF(a.`id_order`, hbd.`id_hotel` IN ('.implode(',', $acsHtls).'), 1)';
} else {
$this->access_where = ' WHERE a.`id_order` = 0';
}
$this->fields_list = array(
@@ -79,12 +93,19 @@ class AdminCustomerThreadsControllerCore extends AdminController
'customer' => array(
'title' => $this->l('Customer'),
'filter_key' => 'customer',
'callback' => 'getCustomerLink',
'tmpTableFilter' => true,
),
'email' => array(
'title' => $this->l('Email'),
'filter_key' => 'a!email',
),
'phone' => array(
'title' => $this->l('Phone'),
'filter_key' => 'a!phone',
'optional' => true,
'visible_default' => true,
),
'contact' => array(
'title' => $this->l('Type'),
'type' => 'select',
@@ -92,12 +113,21 @@ class AdminCustomerThreadsControllerCore extends AdminController
'filter_key' => 'cl!id_contact',
'filter_type' => 'int',
),
'subject' => array(
'title' => $this->l('Subject'),
'filter_key' => 'a!subject',
'tmpTableFilter' => true,
'maxlength' => 40,
'optional' => true,
'visible_default' => true,
),
'language' => array(
'title' => $this->l('Language'),
'type' => 'select',
'list' => $language_array,
'filter_key' => 'l!id_lang',
'filter_type' => 'int',
'optional' => true,
),
'status' => array(
'title' => $this->l('Status'),
@@ -118,9 +148,10 @@ class AdminCustomerThreadsControllerCore extends AdminController
'filter_key' => 'messages',
'tmpTableFilter' => true,
'maxlength' => 40,
'optional' => true,
),
'private' => array(
'title' => $this->l('Private'),
'title' => $this->l('Has Private'),
'type' => 'select',
'filter_key' => 'private',
'align' => 'center',
@@ -135,6 +166,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
'title' => $this->l('Last message'),
'havingFilter' => true,
'type' => 'datetime',
'optional' => true,
),
);
@@ -147,22 +179,61 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
$this->shopLinkType = 'shop';
$arr = array();
if ($contacts = Contact::getContacts($this->context->language->id)) {
foreach ($contacts as $contact) {
$arr[] = array('email_message' => $contact['id_contact'], 'name' => $contact['name']);
}
}
$this->fields_options = array(
'contact' => array(
'title' => $this->l('Contact options'),
'fields' => array(
'PS_CUSTOMER_SERVICE_FILE_UPLOAD' => array(
'title' => $this->l('Allow file uploading'),
'hint' => $this->l('Allow customers to upload files using the contact page.'),
'type' => 'bool'
),
'title' => $this->l('Allow file uploading'),
'hint' => $this->l('Allow customers to upload files using the contact page.'),
'type' => 'bool'
),
'PS_MAIL_EMAIL_MESSAGE' => array(
'title' => $this->l('Send email to'),
'desc' => $this->l('Where customers send messages from the order page.'),
'validation' => 'isUnsignedId',
'type' => 'select',
'cast' => 'intval',
'identifier' => 'email_message',
'list' => $arr
),
'PS_CUSTOMER_SERVICE_CONTACT_ALLOW' => array(
'title' => $this->l('Allow guest to select contact'),
'hint' => $this->l('Allow guest to select the contact while submitting message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_EMAIL_MESSAGE' => array(
'title' => $this->l('Send email to'),
'desc' => $this->l('Where customers send messages from the Contact page.'),
'validation' => 'isUnsignedId',
'type' => 'select',
'cast' => 'intval',
'identifier' => 'email_message',
'list' => $arr
),
'PS_CUSTOMER_SERVICE_NAME_REQUIRED' => array(
'title' => $this->l('Set Name as required'),
'hint' => $this->l('Set Name as required while submitting message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_PHONE_REQUIRED' => array(
'title' => $this->l('Set Phone as required'),
'hint' => $this->l('Set Phone as required while submitting message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_SIGNATURE' => array(
'title' => $this->l('Default message'),
'hint' => $this->l('Please fill out the message fields that appear by default when you answer a thread on the customer service page.'),
'type' => 'textareaLang',
'lang' => true
)
'title' => $this->l('Default message'),
'hint' => $this->l('Please fill out the message fields that appear by default when you answer a thread on the customer service page.'),
'type' => 'textareaLang',
'lang' => true
)
),
'submit' => array(
'title' => $this->l('Save'),
@@ -242,6 +313,25 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
parent::__construct();
$this->_new_list_header_design = true;
$this->list_no_link = true;
}
public function getCustomerLink($customer, $tr)
{
if ($tr['id_customer']) {
return '<a href="'.$this->context->link->getAdminLink('AdminCustomers').'&viewcustomer&id_customer='.$tr['id_customer'].'">'.$customer.' (#'.$tr['id_customer'].')</a>';
}
return $customer;
}
public function setMedia()
{
parent::setMedia();
if (!$this->display || $this->display == 'list') {
$this->addJS(_PS_JS_DIR_.'admin/customer_thread.js');
}
}
public function renderList()
@@ -253,7 +343,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->addRowAction('delete');
$this->_select = '
CONCAT(c.`firstname`," ",c.`lastname`) as customer, cl.`name` as contact, l.`name` as language, group_concat(message) as messages, cm.private,
IF(a.`user_name`="", CONCAT(c.`firstname`," ",c.`lastname`), a.`user_name`) AS customer, cl.`name` as contact, l.`name` as language, group_concat(message) as messages, cm.private,
(
SELECT IFNULL(CONCAT(LEFT(e.`firstname`, 1),". ",e.`lastname`), "--")
FROM `'._DB_PREFIX_.'customer_message` cm2
@@ -274,10 +364,6 @@ class AdminCustomerThreadsControllerCore extends AdminController
LEFT JOIN `'._DB_PREFIX_.'contact_lang` cl
ON (cl.`id_contact` = a.`id_contact` AND cl.`id_lang` = '.(int)$this->context->language->id.')';
if ($id_order = Tools::getValue('id_order')) {
$this->_where .= ' AND id_order = '.(int)$id_order;
}
$this->_group = 'GROUP BY cm.id_customer_thread';
$this->_orderBy = 'id_customer_thread';
$this->_orderWay = 'DESC';
@@ -286,19 +372,42 @@ class AdminCustomerThreadsControllerCore extends AdminController
$categories = Contact::getCategoriesContacts();
$params = array(
$this->l('Total threads') => $all = CustomerThread::getTotalCustomerThreads(),
$this->l('Threads pending') => $pending = CustomerThread::getTotalCustomerThreads('status LIKE "%pending%"'),
$this->l('Total number of customer messages') => CustomerMessage::getTotalCustomerMessages('id_employee = 0'),
$this->l('Total number of employee messages') => CustomerMessage::getTotalCustomerMessages('id_employee != 0'),
$this->l('Unread threads') => $unread = CustomerThread::getTotalCustomerThreads('status = "open"'),
$this->l('Closed threads') => $all - ($unread + $pending)
$stats = array(
array(
'title' => $this->l('Total threads'),
'val' => $all = CustomerThread::getTotalCustomerThreads(),
),
array(
'title' => $this->l('Threads pending'),
'val' => $pending = CustomerThread::getTotalCustomerThreads(
'status='.(int) CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1.'
OR status='.(int) CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2
),
),
array(
'title' => $this->l('Total number of customer messages'),
'val' => CustomerMessage::getTotalCustomerMessages('id_employee = 0'),
),
array(
'title' => $this->l('Total number of employee messages'),
'val' => CustomerMessage::getTotalCustomerMessages('id_employee != 0'),
),
array(
'title' => $this->l('Unread threads'),
'val' => $unread = CustomerThread::getTotalCustomerThreads(
'status='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
),
),
array(
'title' => $this->l('Closed threads'),
'val' => $all - ($unread + $pending),
),
);
$this->tpl_list_vars = array(
'contacts' => $contacts,
'categories' => $categories,
'params' => $params
'stats' => $stats
);
return parent::renderList();
@@ -342,10 +451,9 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
}
if ($id_status = (int)Tools::getValue('setstatus')) {
$status_array = array(1 => 'open', 2 => 'closed', 3 => 'pending1', 4 => 'pending2');
Db::getInstance()->execute('
UPDATE '._DB_PREFIX_.'customer_thread
SET status = "'.$status_array[$id_status].'"
SET status = "'.(int) $id_status.'"
WHERE id_customer_thread = '.(int)$id_customer_thread.' LIMIT 1
');
}
@@ -363,7 +471,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
LEFT OUTER JOIN '._DB_PREFIX_.'customer c
ON (c.email = ct.email)
WHERE ct.id_customer_thread = '.(int)Tools::getValue('id_customer_thread').'
ORDER BY cm.date_add DESC
ORDER BY cm.date_add, cm.id_employee, cm.private DESC
');
$output = $this->displayMessage($messages, true, (int)Tools::getValue('id_employee_forward'));
$cm = new CustomerMessage();
@@ -395,7 +503,8 @@ class AdminCustomerThreadsControllerCore extends AdminController
$employee->firstname.' '.$employee->lastname,
$current_employee->email,
$current_employee->firstname.' '.$current_employee->lastname,
null, null, _PS_MAIL_DIR_, true)) {
null, null, _PS_MAIL_DIR_, true)
) {
$cm->id_employee = (int)$employee->id;
$cm->private = 1;
$cm->message = $this->l('Message forwarded to').' '.$employee->firstname.' '.$employee->lastname."\n".$this->l('Comment:').' '.$message;
@@ -424,7 +533,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->errors[] = '<div class="alert error">'.Tools::displayError('The email address is invalid.').'</div>';
}
}
if (Tools::isSubmit('submitReply')) {
if (Tools::isSubmit('submitReply') || Tools::isSubmit('submitReplyAndClose')) {
$ct = new CustomerThread($id_customer_thread);
ShopUrl::cacheMainDomainForShop((int)$ct->id_shop);
@@ -446,14 +555,26 @@ class AdminCustomerThreadsControllerCore extends AdminController
$file_attachment['mime'] = $_FILES['joinFile']['type'];
}
$customer = new Customer($ct->id_customer);
if ($ct->id_order) {
$link = Tools::url(
$this->context->link->getPageLink('order-detail', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'id_order='.$ct->id_order
);
} else {
$link = Tools::url(
$this->context->link->getPageLink('contact', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'token='.$ct->token
);
}
$headingText = $this->l('Hi');
if ($customer->id) {
$headingText .= ' '.$customer->firstname.' '.$customer->lastname;
}
$params = array(
'{reply}' => Tools::nl2br(Tools::getValue('reply_message')),
'{link}' => Tools::url(
$this->context->link->getPageLink('contact', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'id_customer_thread='.(int)$ct->id.'&token='.$ct->token
),
'{firstname}' => $customer->firstname,
'{lastname}' => $customer->lastname
'{link}' => $link,
'{headingtext}' => $headingText
);
//#ct == id_customer_thread #tc == token of thread <== used in the synchronization imap
$contact = new Contact((int)$ct->id_contact, (int)$ct->id_lang);
@@ -471,8 +592,12 @@ class AdminCustomerThreadsControllerCore extends AdminController
'reply_msg',
sprintf(Mail::l('An answer to your message is available #ct%1$s #tc%2$s', $ct->id_lang), $ct->id, $ct->token),
$params, Tools::getValue('msg_email'), null, $from_email, $from_name, $file_attachment, null,
_PS_MAIL_DIR_, true, $ct->id_shop)) {
$ct->status = 'closed';
_PS_MAIL_DIR_, true, $ct->id_shop)
) {
if (Tools::isSubmit('submitReplyAndClose')) {
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED;
}
$ct->update();
}
Tools::redirectAdmin(
@@ -481,7 +606,24 @@ class AdminCustomerThreadsControllerCore extends AdminController
} else {
$this->errors[] = Tools::displayError('An error occurred. Your message was not sent. Please contact your system administrator.');
}
} else if (Tools::isSubmit('submitReplyAsPrivate')) {
$ct = new CustomerThread($id_customer_thread);
ShopUrl::cacheMainDomainForShop((int)$ct->id_shop);
$cm = new CustomerMessage();
$cm->id_employee = (int)$this->context->employee->id;
$cm->id_customer_thread = $ct->id;
$cm->ip_address = (int)ip2long(Tools::getRemoteAddr());
$cm->message = Tools::getValue('reply_message');
$cm->private = 1;
$cm->read = 1;
if (($error = $cm->validateField('message', $cm->message, null, array(), true)) !== true) {
$this->errors[] = $error;
} elseif (!$cm->add()) {
$this->errors[] = Tools::displayError('Some error occured while saving the message');
}
}
}
return parent::postProcess();
@@ -603,64 +745,54 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
}
$next_thread = CustomerThread::getNextThread((int)$thread->id);
$contacts = Contact::getContacts($this->context->language->id);
$actions = array();
if ($next_thread) {
$next_thread = array(
'href' => self::$currentIndex.'&id_customer_thread='.(int)$next_thread.'&viewcustomer_thread&token='.$this->token,
'name' => $this->l('Reply to the next unanswered message in this thread')
);
}
if ($thread->status != 'closed') {
$actions['closed'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=2&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Mark as "handled"'),
'name' => 'setstatus',
'value' => 2
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED
);
} else {
$actions['open'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Re-open'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
if ($thread->status != 'pending1') {
$actions['pending1'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=3&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1.'&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
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1
);
} else {
$actions['pending1'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Disable pending status'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
if ($thread->status != 'pending2') {
$actions['pending2'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=4&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2.'&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
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2
);
} else {
$actions['pending2'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Disable pending status'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
@@ -710,7 +842,6 @@ class AdminCustomerThreadsControllerCore extends AdminController
'messages' => $messages,
'first_message' => $first_message,
'contact' => $contact,
'next_thread' => $next_thread,
'orders' => isset($orders) ? $orders : false,
'customer' => isset($customer) ? $customer : false,
'products' => isset($products) ? $products : false,
@@ -721,10 +852,6 @@ class AdminCustomerThreadsControllerCore extends AdminController
'timeline_items' => $timeline_items,
);
if ($next_thread) {
$this->tpl_view_vars['next_thread'] = $next_thread;
}
return parent::renderView();
}
@@ -737,10 +864,15 @@ class AdminCustomerThreadsControllerCore extends AdminController
$content = '';
if (!$message['private']) {
$content .= $this->l('Message to: ').' <span class="badge">'.(!$message['id_employee'] ? $message['subject'] : $message['customer_name']).'</span><br/>';
if ($message['id_employee']) {
$content .= $this->l('Message to: ').' <span class="badge">'.($message['customer_name']).'</span><br/><br/>';
}
} else {
$content .= '<span class="label label-info">'.$this->l('Private').'</span><br/><br/>';
}
if (Validate::isLoadedObject($product)) {
$content .= '<br/>'.$this->l('Room type: ').'<span class="label label-info">'.$product->name.'</span><br/><br/>';
$content .= $this->l('Room type: ').'<span class="label label-info">'.$product->name.'</span><br/><br/>';
}
$content .= Tools::safeOutput($message['message']);
@@ -1079,7 +1211,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
$ct->id_contact = $id_contact;
$ct->id_lang = (int)Configuration::get('PS_LANG_DEFAULT');
$ct->id_shop = $this->context->shop->id; //new customer threads for unrecognized mails are not shown without shop id
$ct->status = 'open';
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$ct->token = Tools::passwdGen(12);
$ct->add();
} else {