[modRetailCRM 2.0] - глобальное обновление

Представляю крупное обновление компонента для связи вашего сайта с RetailCRM — modRetailCRM 2.0.
Это одно большое изменение, так как компонент по сути написан с нуля. Это связано с тем, что разработчики RetailCRM сменили версию API и полностью переписали библиотеку, которая используется в компоненте.



Внимание!


Обновление с версии 1.x.x до версии 2.0 невозможно и скорее всего сломает ваши наработки, так как, компонент работает совсем по другому, за что приношу свои извинения.
Если вы использовали стандартный функционал, правильнее будет удалить старую версию, и заново настроить новую. Если вы использовали нестандартный функционал, работающий через API ядра — вам лучше проконсультироваться с автором компонента и программистом.

Что нового в modRetailCRM версии 2.0


  1. Компонент переведен на с 4 на 5 версию API RetailCRM и использует обновленную библиотеку
  2. В связи с новой библиотекой изменен вызов всех методов, используемых в старых версиях компонента
  3. Полностью переписан плагин, отвечающий за передачу заказа из Minishop2 в RetailCRM. Код стал более чистым, гибким и быстрым
  4. Исправлена куча багов
  5. Плагин дополнен множеством функциональных возможностей, учтены пожелания всех обратившихся за техподдержкой пользователей

Что нового в обновленной версии API RetailCRM


Главное изменение — возможность создавать задачи, а не только заказы и клиентов.
Теперь вы можете создавать задачи прямо через сайт.
Например, популярная форма обратного звонка при добавлении обработчика создаст в CRM задачу Перезвонить клиенту по указанному номеру, присвоит исполнителя и назначит дату выполнения.
Пример кода смотрите ниже

Установка и первоначальная настройка


1. Компонент доступен для установки в репозитарии ModStore
2. После установки Вам нужно заполнить в системных настройках следующие поля:


3. Опционально, если вы хотите чтобы в CRM передавались способы оплаты и доставки вам нужно сходить в соответствующие разделы справочника RetailCRM, скопировать оттуда символьные коды способов доставки и оплаты


Эти символьные коды я предлагаю вставить в описание соответсвующих способов доставки и оплаты minishop2


Вы без проблем можете использовать и собственное поле, это легко поправить в плагине.

После этого плагин по идее уже может передавать заказы в RetailCRM

Какие данные передаются в RetailCRM из корзины при оформлении заказа


1. Товары (Наименование, стоимость, количество товара, все используемые в заказе опции товара)
2. Имя, телефон, email клиента и его id по которому (в том числе) CRM умеет группировать заказы
3. Адрес, в его классическом оформлении (Индекс, Область, город, Улица, дом, квартира), а также любые другие поля сохраняемые в таблице адрес заказа.
4. Комментарий клиента
5. Способ и сумма доставки, способ оплаты (если вы указали соответствие справочников)

Можно передавать еще много чего, все достаточно легко настраивается и кастомизируется под специфику магазина. Все принимаемые CRM поля можно посмотреть здесь

Также плагин при создании нового заказа создает и нового клиента в RetailCRM (если конечно такого клиента еще нет) — это осталось без изменений со старой версии компонента

Пример быстрого заказа


Все вышеописанное касается работы minishop2.
Если же вы хотите быстро создать заказ, минуя корзину, например посредством формы быстрого заказа
я предлагаю следующее решение.
Скорее всего вы используете форму работающую через связку formIt + ajaxForm. Клиент заполняет имя, email, телефон, вы получаете через скрытое поле адрес страницы или название продукта и заявка падает вам на почту.
Я написал хук для formIt, который одновременно с отправкой на почту заявки, создает заказ в RetailCRM (собственно это никак не привязано к отправке заказа на email и может работать отдельным решением)

Итак создаем сниппет RetailCRM_QuickOrder_Hook примерно следующего содержания

<?php
if (!$modx->getService('modretailcrm','modRetailCrm', MODX_CORE_PATH.'components/modretailcrm/model/modretailcrm/')) {  
    $modx->log(1, '[RetailCrm] - Not found class RetailCrm');
    return;
}

$pdo = $modx->getService('pdoFetch');

$site = $modx->getOption('modretailcrm_siteCode');
$apiKey = $modx->getOption('modretailcrm_apiKey');
$crmUrl = $modx->getOption('modretailcrm_url');

$modRetailCrm = new modRetailCrm($modx, $apiKey, $crmUrl, $site);

//Принимаю необходимый массив полей из формы
$allFormFields = $hook->getValues();
$order = array();
//Небольшая валидация по вкусу
foreach($allFormFields as $key => $field){
    if(empty($field)) continue;
    switch($key){
        case 'externalId':
        case 'quantity':
            $order[$key] = trim(filter_var($field, FILTER_VALIDATE_INT));
            break;
        case 'email':
            $order[$key] = trim(filter_var($field, FILTER_VALIDATE_EMAIL));
            break;
        case 'pageId':
            break;
        default:
            $order[$key] = trim(filter_var($field, FILTER_SANITIZE_STRING));
    }
    
}
//Отметка Требуется перезвонить
$order['call'] = 1;
$order['orderMethod'] = 'one-click';
$product = $pdo->getArray('msProduct', array("class_key" => 'msProduct', 'id' => $order['externalId']), 
    array("limit" => 1, "offset" => 0,
    'innerJoin' => array(
        'Data' => array(
            'class' => 'msProductData',
            'on' => 'msProduct.id = Data.id'
        )
    ),
    'select' => array(
        'msProduct' => '*',
        'Data' => '*'
    ),
    ));
$order['items'][] = array(
    'initialPrice' => $product['price'],
    'purchasePrice' => $product['price'],
    'productName' => $product['pagetitle'],
    'quantity' => $order['quantity'],
    'offer' => array('externalId' => $product['id'])
);
$response = $modRetailCrm->request->ordersCreate($order, $site);
Данный сниппет принимает несколько полей из формы быстрого заказа, и отправляет эти данные в CRM
Обязательные поля, которые должны быть в форме:
  • firstName
  • phone
  • externalId — id товара
Необязательные поля, которые могут быть форме и передаются в CRM:
  • lastName
  • email
  • quantity — кол-во товара
  • customerComment — комментарий клиента
В принципе можно использовать и другие поля, но я что то не разу не встречал других форм быстрого заказа

Создав сниппет, вы можете добавить его как хук к вызову ajaxForm
{'ajaxForm' | snippet : [
   'form' => '@INLINE
        <form>
            <input class="form-control" type="text" name="firstName" value="" placehoder="Имя">
            <input class="form-control" type="text" name="lastName" value="" placehoder="Фамилия">
            <input class="form-control" type="text" name="phone" value="" placehoder="Телефон">
            <input class="form-control" type="text" name="email" value="" placehoder="Email">
            <input class="form-control" type="text" name="quantity" value="1" placehoder="Количество товара">
            <textarea class="form-control" name="customerComment"></textarea>
            <button type="submit" class="button">Заказ в один клик</button>
            <input type="hidden" name="externalId" value="[[*id]]">
        </form>
   ',
    'hooks' => 'RetailCRM_QuickOrder_Hook',
    'validate' => 'firstName:required,phone:required,externalId:required',
    
]}
Данный пример легко переписать под собственные нужды и совсем не обязательно использовать для этого formIt. Вы можете немного адаптировать прием данных от клиента в свой собственный сниппет и дальше передавать их по назначению используя пример.

Создание задачи в RetailCRM


Ситуация аналогичная быстрому заказу.
Пишем сниппет RetailCRM_Callback_Hook, который будет являться хуком для formIt
Принимает данные из формы, и отправляем в CRM

<?php
if (!$modx->getService('modretailcrm','modRetailCrm', MODX_CORE_PATH.'components/modretailcrm/model/modretailcrm/')) {  
    $modx->log(1, '[RetailCrm] - Not found class RetailCrm');
    return;
}

$pdo = $modx->getService('pdoFetch');

$site = $modx->getOption('modretailcrm_siteCode');
$apiKey = $modx->getOption('modretailcrm_apiKey');
$crmUrl = $modx->getOption('modretailcrm_url');

$modRetailCrm = new modRetailCrm($modx, $apiKey, $crmUrl, $site);

//Принимаю необходимый массив полей из формы
$task = array();
$name = trim(filter_var($hook->getValue('name'), FILTER_SANITIZE_STRING));
$task['phone'] = trim(filter_var($hook->getValue('phone'), FILTER_SANITIZE_STRING));
$task['text'] = 'Требуется позвонить клиенту по имени '.$name;
$task['performerId'] = 5;
$response = $modRetailCrm->request->tasksCreate($task, $site);

В данном случае есть один нюанс, который важно понимать.
Если при создании заказа в CRM ответственное лицо назначать необязательно, то в задаче это важно.
Без указания ответственного — задача просто не создастся.
За это отвечает параметр performerId которому мы присваиваем Номер менеджера в системе CRM
Я указываю его прямо в сниппете, подразумевая что менеджер в системе один, вы можете указать его по своей логике.
Навскидку
1. Указать в вызове сниппета
2. Указать в скрытом поле в форме
3. Указать менеджера по расписанию или еще как то, в зависимости от дня недели, товара. страницы и т.п.

Полный перечень полей, которые можно передать для создания заказа можно посмотреть здесь

Кастомизация и индивидуальные решения


Ядро компонента довольно гибкое и имеет много возможностей, нужду в которых я на практике пока не встречал.
Но если в вашем магазине нужно передавать в CRM какие то нестандартные данные или передавать их нестандартным способом — все довольно легко настраивается, собственно не обязательно использовать minishop2 или вообще не обязательно иметь интернет-магазин. Вы можете управлять клиентами, задачами, расходами а также интегрировать сайт с различными службами доставок, телефонией, учетными системами вроде 1С и т.п.
Основа для всего этого заложена, а дальше требуются лишь умелые руки и немного фантазии.

Оплата


Я принял решение сделать компонент бесплатным, хотя и вложил в него немало сил и времени, но
предлагаю свои услуги по индивидуальной интеграции modRetailCRM в ваши магазины и сервисы. Стоимость и условия сотрудничества обсудим с каждым индивидуально.
Николай Савин
18 ноября 2017, 22:32
modx.pro
3
3 573
+7
Поблагодарить автора Отправить деньги

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

Андрей
20 ноября 2017, 14:01
0
А можно из коробки запилить обновление статусов заказов?
    Николай Савин
    20 ноября 2017, 23:27
    1
    0
    Да конечно можно, вот собственно готовый пример
    case 'msOnChangeOrderStatus':
            if($status == 2){
                $orderData = array();
                $orderData['externalId'] = $order->id;
                $orderData['status'] = 'complete';
                $response = $modRetailCrm->request->ordersEdit($orderData, $by = 'externalId', $site); 
            }
            
            break;
    Дописываем плагин modRetailCRM, добавляя событие отслеживания статусов заказа msOnChangeOrderStatus
    Вам нужно указать id заказа для того чтобы найти нужный заказ в CRM и символьный код статуса в CRM.
    Статусы могут быть разные, это все индивидуально для вашей CRM, список статусов и их символьные коды можно посмотреть в разделе Администрирование — Статусы.
    В данном примере я указал соотношение для статуса оплачено в Minishop — статус Выполнен в CRM
    Николай Савин
    26 ноября 2017, 01:52
    0
    После обновления modRetailCRM до версии 2.0 заказы перестали отправляться в CRM.
    С таким вопросом обратился ко мне пользователь.
    Для решения проблемы я открыл плагин modRetailCRM, нашел строчку, которая отвечает за отправку заказа в RetailCRM
    $response = $modRetailCrm->request->ordersCreate($orderData, $site);
    Результат выполнения команды сохраняется в переменную $response.
    Чтобы просмотреть возможную ошибку (а к слову RetailCRM присылает подробное описание ошибки на русском языке) достаточно распечатать эту переменную и сохранить ее в лог журнал MODX
    $modx->log(1, '[modRetailCRM]  Order Response '.print_r($response, true));
    После создания тестового заказа — пошел посмотреть журнал ошибок и увидел результат выполнения запроса, в котором русским по белому написано, что ошибка возникла из за несуществующего способа оплаты.

    Оказывается если передать в этой строке
    $orderData['payments'][0]['type'] = $order['payment']['description'];
    произвольный текст — RetailCRM не примет заказ.
    Проблема решилась комментированием строки.

    А откуда собственно взялся произвольный текст?
    При создании способов оплаты я предложил в поле Описание — description указывать символьный код способа оплаты из справочника REtailCRM. Например Оплата наличными — cash.
    Если же указать там именно произвольное описание способа оплаты, чтобы показать его клиенту в корзине — получится ошибка, которая решается всего лишь исключением передачи способа оплаты в CRM.

    Для чего я так подробно все расписал.
    Ну помимо очевидного решения бага я надеюсь будет полезна сама методика выявления возможных ошибок.
      Василий Stepanov
      30 января 2018, 18:49
      0
      Со свойствами понял: их у меня у товаров просто нет. Но тогда возникает вопрос: как передать артикул, чтобы он в crm был именно артикулом, а не свойством
      Василий Stepanov
      30 января 2018, 18:36
      0
      Заказы передаются, покупатели тоже.
      Не создается товар складе и не указываются свойства товара.
      Для примера в плагине написал
      $orderData['items'][$key]['properties'][] = array('name' => 'Артикул', 'value' => '06512');
      так сработало, свойство «Артикул» появилось.

      Подскажите, что не так делаю?
        Николай Савин
        30 января 2018, 19:04
        0
        Не понял проблему. Вы же пишете что свойство появилось? Значит все так делаете.
          Василий Stepanov
          30 января 2018, 19:07
          0
          Спасибо, что обратили внимание на мой комментарий.
          Основная проблема в том, что в crm не создаются товары на складе
            Василий Stepanov
            30 января 2018, 19:20
            0
            И артикул добавил через свойства (зел.), как вписать его в поле Артикул crm (красн.)?
              Николай Савин
              31 января 2018, 09:17
              +1
              Насколько я понимаю, вам нужно отдельно создать товар в базе CRM со всеми необходимыми свойствами и привязывать заказ к нему через externalID (идентификатор товара в CRM)
              Это можно сделать вручную, или при создании заказа в магазине настроить запрос существования товара, в случае если товар еще не существует — запускать отдельный метод создания товара.
              В модуле готового кода нет, но вообще все довольно не сложно, если хоть немного понимаете в программировании.
              Рекомендую обратить внимание на документацию к RetailCRM — там все расписано, какие данные в каком случае принимаются
                Василий Stepanov
                31 января 2018, 09:21
                0
                Спасибо, как раз созданием товара и занимаюсь. Поделюсь, если что получится.
                  Василий Stepanov
                  04 февраля 2018, 01:32
                  0
                  Решил так.
                  1. Сформировал ICML-файл, согласно документации
                  В моем случае параметры id и productId в элементах
                  <offer>
                  имеют значение id товара на сайте.
                  2. Загрузил его в базу товаров RetailCRM (Администрирование -> Магазины -> Ваш_магазин -> Каталог: указываем url вашего ICML-файла, ставим галку «Загрузить каталог из ICML сейчас» и жмем сохранить).
                  И так нужно делать каждый раз, после добавления товаров на вашем сайте (другого решения пока не нашел)
                  3. В плагине modRetailCRM заменил этот код
                  foreach ($order['products'] as $key=>$product) {
                     /* данные товаров в заказе */
                  }

                  на этот
                  foreach ($order['products'] as $key=>$product) {
                     $orderData['items'][$key]['initialPrice'] = $product['price'];
                     $orderData['items'][$key]['purchasePrice'] = $product['price'];
                     $orderData['items'][$key]['offer']['externalId'] = $product['product_id'];
                     $orderData['items'][$key]['quantity'] = $product['count'];
                  }

                  Теперь при создании заказа, товары добавляются в заказ из базы crm в соответствии с id товара в моем магазине
            Алексей Андреев
            22 марта 2018, 12:02
            0
            У меня при включении modRetailCRM перестают отправляться заказы, т.е. они в админке создаются, но на странице оформления не выдаётся сообщение об успешной отправке заказа, на почту тоже не приходят.
            В логах следующее:
            [21-Mar-2018 21:51:25 Europe/Moscow] PHP Fatal error:  Uncaught RetailCrm\Exception\CurlException: Couldn't resolve host 'tivoli-market.retailcrm.ru' in /web/core/components/modretailcrm/model/modretailcrm/retailcrm/api-client-php/lib/RetailCrm/Http/Client.php:123
            Stack trace:
            #0 /web/core/components/modretailcrm/model/modretailcrm/retailcrm/api-client-php/lib/RetailCrm/Methods/V4/Orders.php(92): RetailCrm\Http\Client->makeRequest('/orders/create', 'POST', Array)
            #1 /web/core/cache/includes/elements/modplugin/22.include.cache.php(92): RetailCrm\Client\ApiVersion5->ordersCreate(Array, 'tivoli-market-r...')
            #2 /web/core/model/modx/modscript.class.php(70): include('/web/core/cache...')
            #3 /web/core/model/modx/modx.class.php(1609): modScript->process(NULL)
            #4 /web/core/components/minishop2/model/minishop2/minishop2.class.php(987): modX->invokeEvent('msOnCreateOrder', Array)
            #5 /web/core/components/minishop2/model/minishop2/msorderhandler.class.php(449): miniShop2->invokeEvent('msOnCreateOrder', Array)
            #6 /web/core/components/minishop2/model/minishop2/minishop2.class. in /web/core/components/modretailcrm/model/modretailcrm/retailcrm/api-client-php/lib/RetailCrm/Http/Client.php on line 123
            Из-за чего может такое быть?
            Денис
            30 марта 2018, 15:07
            +1
            Николай, приветствую. А как сделать решение для обновления статусов заказа в ms2 из CRM.
            На ум приходит только вешать на событие (какое?), типа открываем в админке заказы и статусы подгружаются из CRM. И второе событие: покупатель открывает в ЛК историю заказов и статусы из CRM обновляются…
            Можно ещё через cron / legacy framework, но это для меня сложно.

            Может уже делал такую задачу?
              Николай Савин
              31 марта 2018, 12:24
              0
              Да ты прав, задача актуальная, делать не приходилось, но люди все чаще интересуются этим.
              я как раз формирую для себя список обновлений, включу и этот вопрос тоже.
              Denis
              31 октября 2018, 16:13
              0
              Здравствуйте.
              Вставил на сайт хук для передачи данных с formit, Данные в crm не приходят. Ошибок в консоле нет.
              Не могу понять что не так.

              <?php
              if (!$modx->getService('modretailcrm','modRetailCrm', MODX_CORE_PATH.'components/modretailcrm/model/modretailcrm/'));  
                  $modx->log(1, '&#91;RetailCrm&#93; - Not found class RetailCrm');
                  return;
              
              $pdo = $modx->getService('pdoFetch');
              
              $site = $modx->getOption('modretailcrm_siteCode');
              $apiKey = $modx->getOption('modretailcrm_apiKey');
              $crmUrl = $modx->getOption('modretailcrm_url');
              
              $modRetailCrm = new modRetailCrm($modx, $apiKey, $crmUrl, $site);
              
              //Принимаю необходимый массив полей из формы
              $task = array();
              $name = trim(filter_var($hook->getValue('name'), FILTER_SANITIZE_STRING));
              $task&#91;'phone'&#93; = trim(filter_var($hook->getValue('phone'), FILTER_SANITIZE_STRING));
              $task&#91;'text'&#93; = 'Требуется позвонить клиенту по имени '.$name;
              $task&#91;'performerId'&#93; = 1;
              $response = $modRetailCrm->request->tasksCreate($task, $site);
                Николай Савин
                31 октября 2018, 16:21
                0
                А где собственно запись ответа в лог? Нужно же после запроса еще ответ в лог записать, чтобы посмотреть ошибку
                  Denis
                  31 октября 2018, 18:11
                  0
                  еще бы знать как эту запись сделать )
                    Николай Савин
                    31 октября 2018, 18:13
                    0
                    Выше посмотри. Здесь же в комментариях несколько раз писал уже
                      Денис
                      08 ноября 2018, 10:02
                      0
                      Поставь в конце строчку:
                      $modx->log(1, '[modRetailCRM]  Task Responce '.print_r($response, true));
                      Скорее всего ошибка связана со временем создания задачи. У меня так:
                      $task = array();
                      $date = time()+60; // Время + 60 сек
                      $task['datetime'] = date('Y-m-d H:i', $date);
                  Denis
                  09 ноября 2018, 21:37
                  0
                  [2018-11-09 18:35:30] (ERROR @ public_html/core/cache/includes/elements/modsnippet/42.include.cache.php : 24) [modRetailCRM]  Task Responce RetailCrm\Response\ApiResponse Object
                  (
                      [statusCode:protected] => 201
                      [response:protected] => Array
                          (
                              [success] => 1
                              [id] => 78
                          )
                  
                  )
                  Вот что в логах при отправки формы
                    Николай Савин
                    09 ноября 2018, 21:52
                    0
                    Ну как видишь Статус — Успех. Задача создана, id присвоен
                    Denis
                    10 ноября 2018, 00:13
                    0
                    Да. Задачи создаются, спасибо.
                    Теперь проблема с созданием заказа.
                    Вставил код из начала темы.

                    Первый заказ уходит. А вот последующие отправки вызывают ошибку
                    (
                        [statusCode:protected] => 400
                        [response:protected] => Array
                            (
                                [success] => 
                                [errorMsg] => Order already exists.
                            )
                    )
                    Я так понимаю ругается на то, что товар с таким ID уже есть. Но у меня всего один товар на сайте. Естественно что при каждом оформлении ID будет тот же самый.
                      Николай Савин
                      10 ноября 2018, 08:10
                      0
                      при чем тут товар? Ты должен передавать id заказа, а не товара
                        Denis
                        11 ноября 2018, 19:18
                        0
                        Его рандомом генерировать? Одинаковых id заказа быть не может?
                        Сейчас сделал рандомный ID? всё работает.
                          Николай Савин
                          12 ноября 2018, 09:36
                          0
                          Нет одинаковых id быть не может. В этом и смысл id — уникальный номер заказа в базе данных.
                          Я так понимаю у тебя заказы в базу не пишутся — зачем тогда вообще id указывать? Отправляй просто необходимые данные. Имя клиента, телефон, данные по товару, что там у тебя еще не знаю.
                          Denis
                          11 ноября 2018, 19:47
                          0
                          Можно как-то сделать чтобы ID был одним и тем-же? А то в црм путаница.
                            Denis
                            11 ноября 2018, 19:48
                            0
                          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                          32