[modRetailCRM] - Интеграция с RetailCRM

Представляю модуль для интеграции популярного сервиса RetailCRM с MODX.





Работа из коробки



Небольшой функционал предусмотрен сразу же при установке приложения.
  1. Плагин срабатывающий на событие onUserSave, автоматически создает новый контакт в CRM при регистрации нового пользователя в MODX. Данное событие срабатывает в том числе при оформлении заказа в интернет-магазине.
  2. Плагин срабатывающий на событие msOnCreateOrder, автоматические создает новый заказ в CRM при оформлении заказа в MiniShop2
На данный момент я просто не знаю, какие еще возможности обязательно нужно включить в работу из коробки. Пишите свое мнение в комментариях. Основные повторяющиеся идеи обязательно внедрим.

Ручной режим



Дополнение основано на собственной библиотеке RetailCRM и в ручном режиме поддерживает ВЕСЬ функционал доступный через API RetaiCRM

Для любителей кастомизировать функционал сайта\интернет-магазина под собственные нужды это означает, что вы можете как угодно взаимодействовать с CRM, оперируя любыми данными.
Для этого доступны десятки методов.
Достаточно Вызвать приложение и указать нужны метод
Например:

//Вызываем приложение
if (!$RetailCrm = $modx->getService('RetailCrm','modretailcrm',MODX_CORE_PATH.'components/modretailcrm/model/modretailcrm/')) {
    $modx->log(1, '[RetailCrm] - Not found class RetailCrm');
    return;
} 
//Получаем список всех заказов одного клиента из CRM
$filters = array();
$filter['customer'] = '+7 701 987 65 43';
$orders = $RetailCrm->ordersList($filter);
//Массив $filters - позволяет фильтровать данные, указав, например, номер или Имя клиента.

//Создаем новый контакт
$customer = array();
$customer['firstName'] = 'Федор';
$customer['lastName'] = 'Бондарчук';
$customer['email'] = 'mail@mail.ru';
$customer['phones'][]['number'] =  '+7 701 987 65 43';
$RetailCrm->customersCreate($customer);

Все доступные методы и их параметры вы можете просмотреть в API RetaiCRM. У Сервиса отличная документация.
Точный вызов того или иного метода — можно найти в файле класса ApiClient внутри дополнения.
Как правило метод вроде /api/v4/customers/create вызывается как customersCreate

Основное преимущество — гибкость в разработке


Главная идея — в том, что мы можем используя существующую библиотеку, передавать в CRM любые данные с сайта, и наоборот получать любую информацию из CRM. Возможности очень гибкие. Мы никак не привязаны к MiniShop2, например.
Можно написать собственные сниппеты и плагины работающие с ShopKeeper.
Совсем не обязательно вообще привязываться к интернет-магазинам.
Совсем простой сниппет позволит создавать в CRM контакт при заполнении классической контактной формы на сайте (пример я написал выше).

Предварительная настройка


Естественно у вас должен быть аккаунт в RetailCRM.
В системных настройках сайта (раздел modretailcrm) Вам нужно указать API ключ, адрес вашей CRM и символьный код сайта. Все эти данные Вы можете посмотреть в личном кабинете RetailCRM

Приложение на данный момент находится на модерации в Modstore.pro, на днях будет опубликовано.
Если кому то не терпится попробовать — могу выслать установочный пакет на E-mail.

Обсуждаем компонент в комментариях. Постараюсь ответить на любые вопросы.
Также готов обсудить персональную адаптацию приложения под ваши конкретные проекты.
Николай Савин
05 мая 2017, 20:15
2
2 501
+9

Комментарии: 34

Антон
06 мая 2017, 08:43
0
А есть пример с минишопом?
Нужно передать данные о корзине, заказчике, доставке, оплате.
    Николай Савин
    06 мая 2017, 09:10
    +2
    <?php
    if (!$RetailCrm = $modx->getService('RetailCrm','modretailcrm',MODX_CORE_PATH.'components/modretailcrm/model/modretailcrm/')) {
        $modx->log(1, '[RetailCrm] - Not found class RetailCrm');
        return;
    } 
    $site = $modx->getOption('modretailcrm_siteCode');
    /** @var modX $modx */
    switch ($modx->event->name) {
       
        case 'msOnCreateOrder':
            $Address = $msOrder->getOne('Address');
            $orderData = array();
            $orderData['customer']['externalId'] = $Address->get('user_id');
            //Отправляем данные клиента
            if ($profile = $modx->getObject('modUserProfile', $Address->get('user_id'))) {
                $customer = array();
                $customer['externalId'] =  $Address->get('user_id');
                $customer['firstName'] = $profile->fullname;
                $customer['email'] = $profile->email;
                if(!empty($profile->phone)){
                    $customer['phones'][]['number'] = $profile->phone;
                }
                if(!empty($profile->mobilephone)){
                    $customer['phones'][]['number'] = $profile->mobilephone;
                }
                $response = $RetailCrm->customersCreate($customer, $site);  
            }
            
            $orderData['externalId'] = $Address->get('id');
            $orderData['firstName'] = $Address->get('receiver');
            $orderData['phone'] = $Address->get('phone');
            $orderData['email'] = $Address->get('email');
            
            $Products = $msOrder->getMany('Products');
            
            $items = array();
            $key = 0;
            foreach ($Products as $pr) {
                $options = $pr->toArray();
                $orderData['items'][$key]['initialPrice'] = $pr->get('cost');
                $orderData['items'][$key]['purchasePrice'] = $pr->get('cost');
                $orderData['items'][$key]['productName'] = $pr->get('name');
                $orderData['items'][$key]['quantity'] = $pr->get('count');
                $orderData['items'][$key]['offer']['externalId'] = $pr->get('id');
                $key ++;
    		}
            //Указываем какие поля заполняются в корзине
            $fields = array(
                'index' => 'Индекс', 
                'country' => 'Страна', 
                'region' => 'Регион', 
                'city' => 'Город', 
                'metro' => 'Метро', 
                'street' => 'Улица', 
                'building' => 'Дом', 
                'room' => 'Квартира\офис',
                'comment' => 'Комментарий к адресу'
            );
            $address = '';
            foreach($fields as $field=>$comment){
                if(!empty($Address->get($field))){
                    $address .= $comment.':'.$Address->get($field).' ';
                    $orderData['delivery']['address'][$field] = $Address->get($field);
                }
            }
            $orderData['delivery']['address']['text'] = $address;
    //Оплата и доставка довольно индивидуальны
    //Если надо заполнять данные о сервисе и методе доставки заполняем поля раздела order[delivery] 
    	//Данные об оплате заполняются в order[paymentType] order[paymentStatus] order[paymentDetail]
            $response = $RetailCrm->ordersCreate($orderData, $site);
          
            break;
    }
Hiddenski
06 мая 2017, 10:57
+1
Грац! Шикарное дополнение, особенно нравится НЕпривязка к minishop'у.
Алексей Шумаев
14 мая 2017, 00:00
+1
Николай, у меня после установки лог забит ошибками
... xpdo.class.php : 503) Path specified for package modsendpulse is not a valid or accessible directory: ...
modsendpulse не устанавливал.
    Николай Савин
    15 мая 2017, 14:02
    0
    Спасибо за сигнал. Я использовал заготовку от modsendpulse. Видимо где то забыл заменить адрес каталога.
      Алексей Шумаев
      15 мая 2017, 17:12
      +1
      Вот уж не за что, это Вам — спасибо!
      Заметил, что лог забивается сам по себе, без каких-то действий на сайте (он тестовый у меня). Как будто cron работает )
Алексей Шумаев
24 мая 2017, 16:34
+1
Николай, а будет ли обновление? Чтобы лог отдохнул )
    Николай Савин
    28 мая 2017, 19:30
    0
    Да конечно, на днях будет. Никак не выделю время с работой.
    Николай Савин
    19 июня 2017, 14:54
    0
    Выпустил обновление. Баг устранен.
      Алексей Шумаев
      27 июня 2017, 10:16
      0
      Не совсем:
      (ERROR @ /core/xpdo/xpdo.class.php: 503) Path specified for package modsendpulse is not a valid or accessible directory: core/components/modsendpulse/model/

      Источник здесь: components/modretailcrm/index.class.php
Андрей
26 июля 2017, 22:12
0
Подскажите, а смена статуса в админке магазина передается в retailCRM?
    Николай Савин
    27 июля 2017, 22:00
    0
    Нет в исходном плагине прописана отправка данных пользователя и отправка заказа, у которого соответственно статус Новый.
    Для отправки обновления статуса нужно дописать плагин на событие msOnChangeOrderStatus

    Я честно говоря не знаю, будет ли сейчас работать метод обновления, так как RetailCRM сейчас выпустили обновление API. Что то изменили, что то добавили, некоторые методы убрали. Надо тестировать короче.
Алексей Шумаев
22 августа 2017, 17:25
0
Только руки дошли…
В общем, у кого осталась ошибка с «modsendpulse» в логе после обновления, уберите «modsendpulse» из системной настройки minishop2 «extension_packages»
Алексей Шумаев
05 сентября 2017, 13:53
0
Если не верно указать адрес retailCRM (modretailcrm_urlCrm), то будет
PHP message: PHP Fatal error:  Uncaught Error: Class 'InvalidJsonException' not found in /home/xxx/xxx/xxx/components/modretailcrm/model/modretailcrm/Response/ApiResponse.php:52
Ошибка не очевидная… Наверно желательно добавить проверку нужных настроек в начале + добавить исключение, я так понимаю.
Дмитрий
27 февраля 2018, 10:43
0
Добрый день.
Если пользователь есть в админке после оформления заказа выдает ошибку, данные не передаются в CRM
[customer.externalId] => Customer with externalId=6 not found. Order externalId=43.
Если пользователя нет админке, создает нового и данные в CRM передает

Подскажите как поправить. Спасибо.
    Николай Савин
    27 февраля 2018, 10:47
    0
    Здравствуйте. Надо тестировать, так сходу не подскажу решение.
Oleg Pimanov
19 октября 2018, 12:44
0
Добрый день! Такой вопрос, заказчик захотел подружить «Мой склад» и Retail CRM, при передаче заказа в Retail CRM к заказу надо добавить поле «Внешний код», которое есть в «Мой склад». Точнее не добавить, а вставить вместо id товара, чтобы Мой склад мог понять какой товар куплен и сброшен в заказе в Retail CRM. Это через tv поля можно сделать?
    Николай Савин
    19 октября 2018, 12:55
    0
    Да, почему бы и нет. Правильнее было бы не TV использовать, а добавить новое свойство товара. Это уменьшит время оформления заказа. Ну и подставляйте новый идентификатор в массив товаров, поле externalID, в плагине
Oleg Pimanov
19 октября 2018, 18:11
0
У Вас модуль передает id товара вот этой строчкой

$orderData['items'][$key]['offer']['externalId'] = $product['externalId'];
Для того, чтобы мой склад понял, что упало в заказе в Retail CRM, должно быть типа так

$orderData['items'][$key]['offer']['externalId'] = $product['externalCode'];

При такой настрйоке не срабатывает ничего, заказ не отправляется в Retail CRM
    Николай Савин
    19 октября 2018, 18:38
    0
    В конце плагина есть закомментированная строчка записи результата в лог. Нужно раскомментировать и посмотреть какая будет ошибка
Oleg Pimanov
22 октября 2018, 11:51
0
Добрый день! Такой вопрос, а по какому полю Retail CRM понимаеьт какой товар заказали и сравнивает его с своей структурой, которую мы туда загрузили.
    Николай Савин
    22 октября 2018, 12:51
    0
    Вообще при выгрузке нужно указывать идентификатор товара в поле ExternalID
    Если Вы этого не делали, тогда отдельного специального поля для связи товаров в различных системах нет.

    В таком случае, можно перед передачей заказа в RetailCRM, получить идентификаторы товара, используя поиск по товарам в CRM

    Для этого в плагине, там где начинается перебор товаров заказа, можно добавить дополнительный поисковый запрос в RetailCRM
    $store_response = $modRetailCrm->request->storeProducts(array('name' => $product['article']), 1, 20);  
    if($store_response->getStatusCode() == 200){
            $product_id = $store_response['products'][0]['offers'][0]['id'];
      }
    $orderData['items'][$key]['offer']['id'] = $product_id;
    Здесь вы ищите на складе RetailCRM товар, у которого наименование совпадает с артикулом в minishop2. Если такой товар найден получаем его id.
    Разумеется фокус сработает, если указанные данные у товаров в разных системах один и тот же.
    Никто не мешает искать по другим общим параметрам.

    Важно также понимать, что эти дополнительные запросы занимают время и заказ передается дольше, а клиент в этот момент сидит и смотрит на экран корзины.
      Oleg Pimanov
      08 ноября 2018, 22:28
      0
      Доброй ночи! Выше код, который я прописал, работает, но почему — то предает только правильно количество товара и цену, а название товара передает всегда одно и то же.
        Николай Савин
        09 ноября 2018, 15:43
        0
        В этом случае ни цену, ни название товара вообще передавать не нужно, они есть в базе RetailCRM. Достаточно передавать идентификатор
          Oleg Pimanov
          09 ноября 2018, 15:47
          0
          Я, наверное не так выразился, в самой Retail CRM, когда туда заказ приходит, там в заказе вот имя товара высвечивается одно и то же, хотя остальные параметры (цена и количетсво) как надо.
            Николай Савин
            09 ноября 2018, 15:49
            0
            Ну значит у тебя либо все товары с одним и тем же именем, либо ты передаешь один и тот же id товара
              Oleg Pimanov
              09 ноября 2018, 15:51
              0
              В админке все товары разные и при выгрущке структуры в Retail тоже разные,
                Николай Савин
                09 ноября 2018, 15:52
                0
                Ну значит происходит магия
                  Oleg Pimanov
                  09 ноября 2018, 17:50
                  0
                  Retail CRM связана с «Мой склад», выгрузка структуры в Retail CRM просиходит с помощью мануала от «Мой склад», по идее этот никак не должно влиять на заказ, который приходит из minishop2, но сейчас приходит заказ с одинковым названием, но разными ценами и количеством
Oleg Pimanov
22 октября 2018, 14:07
0
Спасибо, это сработало, для «Мой склад» теперь все передается. Вы можете это добавить себе в Ваш плагин, так как смогие их используют вмесет, «Мой склад» и «Retail CRM»
    Николай Савин
    22 октября 2018, 14:11
    +1
    Хотя бы плюсик к статье поставьте вместе со спасибо,
    По существу Не уверен что такая доработка плагина нужна. Вы пока второй на 130 загрузок, кто такое спрашивает.
      Oleg Pimanov
      22 октября 2018, 14:15
      0
      Ну может быть, к статье не могу поставить плюсик, не дает, а вот к ответу да, могу.