* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ namespace PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryHandler; use Contact; use Context; use Currency; use Customer; use CustomerThread; use DateTime; use Employee; use Order; use PrestaShop\PrestaShop\Core\Domain\CustomerService\Exception\CustomerThreadNotFoundException; use PrestaShop\PrestaShop\Core\Domain\CustomerService\Query\GetCustomerThreadForViewing; use PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryResult\CustomerInformation; use PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryResult\CustomerThreadMessage; use PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryResult\CustomerThreadTimeline; use PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryResult\CustomerThreadTimelineItem; use PrestaShop\PrestaShop\Core\Domain\CustomerService\QueryResult\CustomerThreadView; use PrestaShop\PrestaShop\Core\Domain\CustomerService\ValueObject\CustomerThreadId; use PrestaShop\PrestaShop\Core\Domain\CustomerService\ValueObject\CustomerThreadMessageType; use PrestaShop\PrestaShop\Core\Domain\CustomerService\ValueObject\CustomerThreadStatus; use PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\LanguageId; use Product; use Symfony\Contracts\Translation\TranslatorInterface; use Tools; use Validate; /** * @internal */ class GetCustomerThreadForViewingHandler implements GetCustomerThreadForViewingHandlerInterface { /** * @var Context */ private $context; /** * @var TranslatorInterface */ private $translator; /** * @param Context $context */ public function __construct(Context $context) { $this->context = $context; $this->translator = $context->getTranslator(); } /** * {@inheritdoc} */ public function handle(GetCustomerThreadForViewing $query) { $customerThread = $this->getCustomerThread($query->getCustomerThreadId()); $messages = CustomerThread::getMessageCustomerThreads($query->getCustomerThreadId()->getValue()); return new CustomerThreadView( $query->getCustomerThreadId(), new LanguageId((int) $customerThread->id_lang), $this->getAvailableActions($customerThread), $this->getCustomerInformation($customerThread), $this->getContactName($customerThread), $this->getCustomerThreadMessages($messages), $this->getTimeline($messages, $customerThread) ); } /** * @param array $messages * * @return CustomerThreadMessage[] */ private function getCustomerThreadMessages(array $messages) { $threadMessages = []; foreach ($messages as $key => $message) { $employeeImage = null; if ($message['id_employee']) { $employee = new Employee($message['id_employee']); $employeeImage = $employee->getImage(); } $attachmentFile = null; if (!empty($message['file_name']) && file_exists(_PS_UPLOAD_DIR_ . $message['file_name']) ) { $attachmentFile = $message['file_name']; } $productId = null; $productName = null; if ($message['id_product']) { $product = new Product((int) $message['id_product'], false, $this->context->language->id); if (Validate::isLoadedObject($product)) { $productId = (int) $product->id; $productName = $product->name; } } $type = $message['id_employee'] ? CustomerThreadMessageType::EMPLOYEE : CustomerThreadMessageType::CUSTOMER; $threadMessages[] = new CustomerThreadMessage( $type, $message['message'], $message['date_add'], $employeeImage, $message['employee_name'], $message['customer_name'], $attachmentFile, $productId, $productName ); } return $threadMessages; } /** * @param CustomerThreadId $customerThreadId * * @return CustomerThread */ private function getCustomerThread(CustomerThreadId $customerThreadId) { $customerThread = new CustomerThread($customerThreadId->getValue()); if ($customerThread->id !== $customerThreadId->getValue()) { throw new CustomerThreadNotFoundException(sprintf('Customer thread with id "%s" was not found', $customerThreadId->getValue())); } return $customerThread; } /** * Get customer thread messages in timeline * * @param array $messages * @param CustomerThread $customerThread * * @return CustomerThreadTimeline */ private function getTimeline(array $messages, CustomerThread $customerThread) { $timeline = []; foreach ($messages as $message) { $product = new Product((int) $message['id_product'], false, $this->context->language->id); $content = ''; if (!$message['private']) { $content .= sprintf( '%s %s
', $this->translator->trans('Message to:', [], 'Admin.Catalog.Feature'), !$message['id_employee'] ? $message['subject'] : $message['customer_name'] ); } if (Validate::isLoadedObject($product)) { $content .= sprintf( '
%s%s

', $this->translator->trans('Product:', [], 'Admin.Catalog.Feature'), $product->name ); } $content .= Tools::safeOutput($message['message']); $timeline[$message['date_add']][] = [ 'arrow' => 'left', 'background_color' => '', 'icon' => 'email', 'content' => $content, 'date' => $message['date_add'], 'related_order_id' => null, ]; } $order = new Order((int) $customerThread->id_order); if (Validate::isLoadedObject($order)) { $order_history = $order->getHistory($this->context->language->id); foreach ($order_history as $history) { $link_order = $this->context->link->getAdminLink('AdminOrders', true, [], [ 'vieworder' => 1, 'id_order' => (int) $order->id, ]); $content = sprintf( '%s #%d

', Tools::safeOutput($link_order), $this->translator->trans('Order', [], 'Admin.Global'), $order->id ); $content .= sprintf( '%s %s', $this->translator->trans('Status:', [], 'Admin.Catalog.Feature'), $history['ostate_name'] ); $timeline[$history['date_add']][] = [ 'arrow' => 'right', 'alt' => true, 'background_color' => $history['color'], 'icon' => 'credit_card', 'content' => $content, 'date' => $history['date_add'], 'see_more_link' => $link_order, 'related_order_id' => (int) $order->id, ]; } } krsort($timeline); $timelineItems = []; foreach ($timeline as $items) { foreach ($items as $item) { $timelineItems[] = new CustomerThreadTimelineItem( $item['content'], $item['icon'], $item['arrow'], $item['date'], isset($item['background_color']) ? $item['background_color'] : null, $item['related_order_id'] ); } } return new CustomerThreadTimeline($timelineItems); } /** * @param CustomerThread $thread * * @return array */ private function getAvailableActions(CustomerThread $thread) { $actions = []; if ($thread->status !== CustomerThreadStatus::CLOSED) { $actions[CustomerThreadStatus::CLOSED] = [ 'label' => $this->translator->trans('Mark as "handled"', [], 'Admin.Catalog.Feature'), 'value' => CustomerThreadStatus::CLOSED, ]; } else { $actions[CustomerThreadStatus::OPEN] = [ 'label' => $this->translator->trans('Re-open', [], 'Admin.Catalog.Feature'), 'value' => CustomerThreadStatus::OPEN, ]; } if ($thread->status !== CustomerThreadStatus::PENDING_1) { $actions[CustomerThreadStatus::PENDING_1] = [ 'label' => $this->translator->trans( 'Mark as "pending 1" (will be answered later)', [], 'Admin.Catalog.Feature' ), 'value' => CustomerThreadStatus::PENDING_1, ]; } else { $actions[CustomerThreadStatus::PENDING_1] = [ 'label' => $this->translator->trans('Disable pending status', [], 'Admin.Catalog.Feature'), 'value' => CustomerThreadStatus::OPEN, ]; } if ($thread->status !== CustomerThreadStatus::PENDING_2) { $actions[CustomerThreadStatus::PENDING_2] = [ 'label' => $this->translator->trans( 'Mark as "pending 2" (will be answered later)', [], 'Admin.Catalog.Feature' ), 'value' => CustomerThreadStatus::PENDING_2, ]; } else { $actions[CustomerThreadStatus::PENDING_2] = [ 'label' => $this->translator->trans('Disable pending status', [], 'Admin.Catalog.Feature'), 'value' => CustomerThreadStatus::OPEN, ]; } return $actions; } /** * @param CustomerThread $thread * * @return CustomerInformation */ private function getCustomerInformation(CustomerThread $thread) { if (!$thread->id_customer) { return CustomerInformation::withEmailOnly($thread->email); } $customer = new Customer($thread->id_customer); $orders = Order::getCustomerOrders($customer->id); $totalOk = 0; $ordersOk = []; if (count($orders)) { 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']) ); } } return new CustomerInformation( $customer->id, $customer->firstname, $customer->lastname, $thread->email, count($ordersOk), $totalOk ? Tools::displayPrice($totalOk, $this->context->currency) : $totalOk, (new DateTime($customer->date_add))->format($this->context->language->date_format_lite) ); } /** * @param CustomerThread $thread * * @return string|null */ private function getContactName(CustomerThread $thread) { $contacts = Contact::getContacts($this->context->language->id); foreach ($contacts as $contact) { if ((int) $contact['id_contact'] === (int) $thread->id_contact) { return $contact['name']; } } return null; } }