Добрый день! Подскажите, пожалуйста, каким образом необходимо корректно формировать SelectRequest, для того что бы получить всю таблицу из бд?



Нашел информацию по этой ссылке:https://academy.terrasoft.ru/documents/technic-sdk/7-12/dataservice-cht…

Тут именно тот код:

//------------------------------------------------------------------------------

// Экземпляр класса запроса.
var selectQuery = new SelectQuery()
{
    // Название корневой схемы.
    RootSchemaName = "Contact",
    // Коллекция колонок запроса.
    Columns = new SelectQueryColumns()
};
// Выражение, задающее тип колонки [[ФИО].
var columnExpressionName = new ColumnExpression()
{
    // Тип выражения — колонка схемы.
    ExpressionType = EntitySchemaQueryExpressionType.SchemaColumn,
    // Путь к колонке.
    ColumnPath = "Name"
};
// Конфигурирование колонки [Name]. 
var selectQueryColumnName = new SelectQueryColumn()
{
    //Заголовок.
    Caption = "ФИО",
    // Направление сортировки — по возрастанию.
    OrderDirection = OrderDirection.Ascending,
    // Позиция порядка сортировки.
    OrderPosition = 0,
    // Выражение, задающее тип колонки.
    Expression = columnExpressionName
};
// Выражение, задающее тип колонки [Количество активностей].
var columnExpressionActivitiesCount = new ColumnExpression()
{
    // Тип выражения — вложенный запрос.
    ExpressionType = EntitySchemaQueryExpressionType.SubQuery,
    // Путь к колонке относительно корневой схемы.
    ColumnPath = "[Activity:Contact].Id",
    // Тип функции — агрегирующая.
    FunctionType = FunctionType.Aggregation,
    // Тип агрегации — количество.
    AggregationType = AggregationType.Count
};

//------------------------------------------------------------------------------

В данном случае, у меня к примеру нет заголовка "ФИО", у меня есть просто столбец "Name"("ФИО" в данном случае это есть то как будут отображаться полученные данные или это именно часть таблицы к которой я обращаюсь?)

В данном случае, авторизация к серверу проходит успешно, но при попытке считать результат выполнения запроса, выпадает ошибка:

"Exception thrown: 'System.Net.WebException' in System.dll"

 

Нравится

9 комментариев
Лучший ответ

engineer7 пишет:

и ошибок он не выдает...

Защиту от CSRF атак выключили? Просто добавление токена не наблюдаю. Попробуйте до вызова 

using (var requestStream = selectRequest.GetRequestStream())

// Добавление CSRF-токена в заголовок запроса.
CookieCollection cookieCollection = AuthCookie.GetCookies(new Uri(authServiceUri));
string csrfToken = cookieCollection["BPMCSRF"].Value;
selectRequest.Headers.Add("BPMCSRF", csrfToken);

 

Указанный пример — это при работе с системой извне. Если просто в серверном коде получать данные из таблицы, см. тут.

Если Вам действительно нужно работать с удалённым сайтом по веб-сервисам, то причина может быть аналогичная этому обсуждению, где сервер возвращает 404.

Зверев Александр пишет:

Указанный пример — это при работе с системой извне. Если просто в серверном коде получать данные из таблицы, см. тут.

Если Вам действительн...

Александр, спасибо за ответ! 



В данном случае, меня интересует второй случай, когда нужно работать с удаленным сайтом по веб-сервисам, но дело в том, что сервер возвращает не 404(нет ответа) ошибку, а 403(проблемы с доступом).



Возможно такое что я не правильно заполняю конфигурирование запроса?

 

// Экземпляр класса запроса.

            var selectQuery = new SelectQuery()

            {

                // Название корневой схемы.

                RootSchemaName = "Contact",

                // Коллекция колонок запроса.

                Columns = new SelectQueryColumns()

            };



            // Выражение, задающее тип колонки [[ФИО].

            var columnExpressionName = new ColumnExpression()

            {

                // Тип выражения — колонка схемы.

                ExpressionType = EntitySchemaQueryExpressionType.SchemaColumn,

                // Путь к колонке.

                ColumnPath = "Name"

            };

            // Конфигурирование колонки [Name].

            var selectQueryColumnName = new SelectQueryColumn()

            {

                //Загловок.

                Caption = "",

                // Направление сортировки — по возрастанию.

                OrderDirection = OrderDirection.Ascending,

                // Позиция порядка сортировки.

                OrderPosition = 0,

                // Выражение, задающее тип колонки.

                Expression = columnExpressionName

            };

            // Выражение, задающее тип колонки [Количество активностей].

            var columnExpressionActivitiesCount = new ColumnExpression()

            {

                // Тип выражения — вложенный запрос.

                ExpressionType = EntitySchemaQueryExpressionType.SubQuery,

                // Путь к колонке относительно корневой схемы.

                ColumnPath = "[Activity:Contact].Id",

                // Тип функции — агрегирующая.

                FunctionType = FunctionType.Aggregation,

                // Тип агрегации — количество.

                AggregationType = AggregationType.Count

            };

            // Конфигурирование колонки [Количество активностей].

            var selectQueryColumnActivitiesCount = new SelectQueryColumn()

            {

                //Загловок.

                Caption = "",

                // Направление сортировки — по возрастанию.

                OrderDirection = OrderDirection.Ascending,

                // Позиция порядка сортировки.

                OrderPosition = 1,

                // Выражение, задающее тип колонки.

                Expression = columnExpressionActivitiesCount

            };

            // Добавление колонок в запрос.

            selectQuery.Columns.Items = new Dictionary<string, SelectQueryColumn>()

            {

                {

                    "Name",

                    selectQueryColumnName

                },

                {

                    "ActivitiesCount",

                    selectQueryColumnActivitiesCount

                }

            };

            // Сериализация экземпляра класса запроса на добавление в JSON-строку.

            var json = new JavaScriptSerializer().Serialize(selectQuery);

            // Преобразование строки JSON-объекта в массив байтов.

            byte[] jsonArray = Encoding.UTF8.GetBytes(json);

            // Создание экземпляра HTTP-запроса.

            var selectRequest = HttpWebRequest.Create(selectQueryUri) as HttpWebRequest;

            // Определение метода запроса.

            selectRequest.Method = "POST";

            // Определение типа содержимого запроса.

            selectRequest.ContentType = "application/json";

            // Добавление полученных ранее аутентификационных cookie в запрос на получение данных.

            selectRequest.CookieContainer = AuthCookie;

            // Установить длину содержимого запроса.

            selectRequest.ContentLength = jsonArray.Length;

            using (var requestStream = selectRequest.GetRequestStream())

            {

                requestStream.Write(jsonArray, 0, jsonArray.Length);

            }

            ResponseStatus status = null;

            // Получение ответа от сервера. Если аутентификация проходит успешно, в свойство AuthCookie будут

            // помещены cookie, которые могут быть использованы для последующих запросов.

            using (var response = (HttpWebResponse)selectRequest.GetResponse())

            {

                using (var reader = new StreamReader(response.GetResponseStream()))

                {

                    // Десериализация HTTP-ответа во вспомогательный объект.

                    string responseText = reader.ReadToEnd();

                    status = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<ResponseStatus>(responseText);

                }

            }

 

В данном случае, конфигурирование колонки [name], каким образом нужно заполнять поле "Caption"?

Так как по умолчанию стояла запись "ФИО", я ее удалил, так как в таблице "Contact" у меня нет данного столбца.

Так же я не очень хорошо понимаю значение выражения, задающее тип колонки (количество активностей).

Пример, по которому пытаюсь получить доступ к БД: https://academy.terrasoft.ru/documents/technic-sdk/7-12/dataservice-cht…

Если с доступом, то, возможно, не передалась кука, полученная от AuthService. Если не уверены в доработках, сначала попробуйте со скопированными без изменений примерами.

Попробовал с исходным кодом:

// ----------------------------------------------

using System;

using System.Text;

using System.IO;

using System.Net;

using System.Collections.Generic;

using Terrasoft.Nui.ServiceModel.DataContract;

using Terrasoft.Core.Entities;

using System.Web.Script.Serialization;

using Terrasoft.Common;

namespace DataServiceSelectExample

{

    class Program

    {

        // Основной URL приложения bpm'online. Необходимо заменить на пользовательский.

        private const string baseUri = @"http://url";

        // Строка запроса к методу Login сервиса AuthService.svc.

        private const string authServiceUri = baseUri + @"/ServiceModel/AuthService.svc/Login";

        // Строка пути запроса SelectQuery.

        private const string selectQueryUri = baseUri + @"/0/DataService/json/SyncReply/SelectQuery";

        // Cookie аутентификации bpm'online.

        private static CookieContainer AuthCookie = new CookieContainer();



        // Метод выполняет аутентификацию пользователя.

        // Параметры:

        // userName — имя пользователя bpm'online,

        // userPassword — пароль пользователя bpm'online.

        private static bool TryLogin(string userName, string userPassword)

        {

            // Создание экземпляра запроса к сервису аутентификации.

            var authRequest = HttpWebRequest.Create(authServiceUri) as HttpWebRequest;

            // Определение метода HTTP-запроса.

            authRequest.Method = "POST";

            // Определение типа контента запроса.

            authRequest.ContentType = "application/json";

            // Включение использования cookie в запросе.

            authRequest.CookieContainer = AuthCookie;

            // Помещение в тело запроса учетной информации пользователя.

            using (var requesrStream = authRequest.GetRequestStream())

            {

                using (var writer = new StreamWriter(requesrStream))

                {

                    writer.Write(@"{

                        ""UserName"":""" + userName + @""",

                        ""UserPassword"":""" + userPassword + @"""

                    }");

                }

            }

            // Получение ответа от сервера. Если аутентификация проходит успешно, в свойство AuthCookie будут

            // помещены cookie, которые могут быть использованы для последующих запросов.

            using (var response = (HttpWebResponse)authRequest.GetResponse())

            {

                if (AuthCookie.Count > 0)

                {

                    return true;

                }

            }

            return false;

        }

        // Главный метод приложения.

        static void Main(string[] args)

        {

            // Если выполнение аутентификации неудачно, приложение завершает работу.

            if (!TryLogin("login", "password"))

            {

                Console.WriteLine("Ошибка аутентификации!");

                return;

            }

            // Экземпляр класса запроса.

            var selectQuery = new SelectQuery()

            {

                // Название корневой схемы.

                RootSchemaName = "Contact",

                // Коллекция колонок запроса.

                Columns = new SelectQueryColumns()

            };

            // Выражение, задающее тип колонки [[ФИО].

            var columnExpressionName = new ColumnExpression()

            {

                // Тип выражения — колонка схемы.

                ExpressionType = EntitySchemaQueryExpressionType.SchemaColumn,

                // Путь к колонке.

                ColumnPath = "Name"

            };

            // Конфигурирование колонки [Name].

            var selectQueryColumnName = new SelectQueryColumn()

            {

                //Загловок.

                Caption = "ФИО",

                // Направление сортировки — по возрастанию.

                OrderDirection = OrderDirection.Ascending,

                // Позиция порядка сортировки.

                OrderPosition = 0,

                // Выражение, задающее тип колонки.

                Expression = columnExpressionName

            };

            // Выражение, задающее тип колонки [Количество активностей].

            var columnExpressionActivitiesCount = new ColumnExpression()

            {

                // Тип выражения — вложенный запрос.

                ExpressionType = EntitySchemaQueryExpressionType.SubQuery,

                // Путь к колонке относительно корневой схемы.

                ColumnPath = "[Activity:Contact].Id",

                // Тип функции — агрегирующая.

                FunctionType = FunctionType.Aggregation,

                // Тип агрегации — количество.

                AggregationType = AggregationType.Count

            };

            // Конфигурирование колонки [Количество активностей].

            var selectQueryColumnActivitiesCount = new SelectQueryColumn()

            {

                //Загловок.

                Caption = "Количество активностей",

                // Направление сортировки — по возрастанию.

                OrderDirection = OrderDirection.Ascending,

                // Позиция порядка сортировки.

                OrderPosition = 1,

                // Выражение, задающее тип колонки.

                Expression = columnExpressionActivitiesCount

            };

            // Добавление колонок в запрос.

            selectQuery.Columns.Items = new Dictionary<string, SelectQueryColumn>()

            {

                {

                    "Name",

                    selectQueryColumnName

                },

                {

                    "ActivitiesCount",

                    selectQueryColumnActivitiesCount

                }

            };

            // Сериализация экземпляра класса запроса на добавление в JSON-строку.

            var json = new JavaScriptSerializer().Serialize(selectQuery);

            // Преобразование строки JSON-объекта в массив байтов.

            byte[] jsonArray = Encoding.UTF8.GetBytes(json);

            // Создание экземпляра HTTP-запроса.

            var selectRequest = HttpWebRequest.Create(selectQueryUri) as HttpWebRequest;

            // Определение метода запроса.

            selectRequest.Method = "POST";

            // Определение типа содержимого запроса.

            selectRequest.ContentType = "application/json";

            // Добавление полученных ранее аутентификационных cookie в запрос на получение данных.

            selectRequest.CookieContainer = AuthCookie;

            // Установить длину содержимого запроса.

            selectRequest.ContentLength = jsonArray.Length;

            // Помещение JSON-объекта в содержимое запроса .

            using (var requestStream = selectRequest.GetRequestStream())

            {

                requestStream.Write(jsonArray, 0, jsonArray.Length);

            }

            // Выполнение HTTP-запроса и получение ответа от сервера.

            using (var response = (HttpWebResponse)selectRequest.GetResponse())

            {

                // Вывод ответа в консоль.

                using (StreamReader reader = new StreamReader(response.GetResponseStream()))

                {

                    Console.WriteLine(reader.ReadToEnd());

                }

            }

            // Задержка выполнения приложения.

            Console.ReadKey();

        }

    }

}

// ----------------------------------------------

На вот этом месте возникает ошибка:

// ----------------------------------------------

// Выполнение HTTP-запроса и получение ответа от сервера.

            using (var response = (HttpWebResponse)selectRequest.GetResponse())

Сложно сказать, не видя конкретных запросов и ответов на них в Fiddler.

Может, логин и пароль не подходит.

В той статье есть ссылки на скачивание готовых файлов примеров программы на C#. Возможно, с ними заработает. 

Зверев Александр,

Это и есть код из той статьи(https://academy.terrasoft.ru/documents/technic-sdk/7-12/dataservice-cht…), единственное что я заменил, это логин и пароль. Они в свою очередь верны, так как вызывается метод tryLogin до вызова SelectRequest, и ошибок он не выдает...

engineer7 пишет:

и ошибок он не выдает...

Защиту от CSRF атак выключили? Просто добавление токена не наблюдаю. Попробуйте до вызова 

using (var requestStream = selectRequest.GetRequestStream())

// Добавление CSRF-токена в заголовок запроса.
CookieCollection cookieCollection = AuthCookie.GetCookies(new Uri(authServiceUri));
string csrfToken = cookieCollection["BPMCSRF"].Value;
selectRequest.Headers.Add("BPMCSRF", csrfToken);

 

Варфоломеев Данила,

Большое спасибо, вопрос решен! Проблема была именно в этом!

Видимо, пример из справки не актуализировали для поддержки CSRF в последних версиях.

Показать все комментарии

Добрый день!

Возник такой вопрос, если пользователь добавил к примеру к себе в аккаунт фотографию, данная фотография находится в БД, каким образом можно скачать фотографию через c# на локальную машину с удаленного сервера? 



Вопрос не только именно на счет фотографий, интересует механизм подобной работы в общем.



На сайте помимио экспорта в excel файлы текста нашел только экспорт настроек(css, js ..), есть ли такая возможность скачать информацию вообще? 

Нравится

4 комментария
Лучший ответ

В базе они хранятся в таблицах вроде ContactFile, AccountFile и т.д.

Скачать можно точно так же, как это делает браузер по нажатию пользователя: послать GET на адрес вроде:

http://адрес_сайта/0/rest/FileService/GetFile/e9eafee9-c4e4-4793-ad0a-003bd2c6a9b4/935b6ecb-3509-4c8a-bc7e-03ab0661da24

Тут первый GUID — ID схемы ContactFile (видно в URL, если её открыть в дизайнере), второй — ID записи в таблице.

В ответ придёт сам файл.

Если это делается не в браузере, где пользователь уже залогинен, то сначала нужно получить куку при помощи AuthService.

 

В базе они хранятся в таблицах вроде ContactFile, AccountFile и т.д.

Скачать можно точно так же, как это делает браузер по нажатию пользователя: послать GET на адрес вроде:

http://адрес_сайта/0/rest/FileService/GetFile/e9eafee9-c4e4-4793-ad0a-003bd2c6a9b4/935b6ecb-3509-4c8a-bc7e-03ab0661da24

Тут первый GUID — ID схемы ContactFile (видно в URL, если её открыть в дизайнере), второй — ID записи в таблице.

В ответ придёт сам файл.

Если это делается не в браузере, где пользователь уже залогинен, то сначала нужно получить куку при помощи AuthService.

 

Александр, большое спасибо! 

В данном случае "rest/FileService/GetFile" это имеющийся сервис у bpm или собственный сервис? 

engineer7 пишет:

это имеющийся сервис у bpm

Да. Это базовый сервис работы с файлами. 

Также, если надо будет дёргать картинки из профилей контактов/контрагентов, то придётся лезть в местную "помойку" с изображениями:

[адрес сайта]/img/entity/hash/SysImage/Data/[id записи из SysImage]

Данила, понял Вас, большое спасибо!

Показать все комментарии

Добрый день.

подскажите как мне сделать выполнения определенного кода старте bpm'online, который регистрирует BPM'Online в определенном сервисе. И так же при выключении BPM'Online делает unregister.

Нравится

1 комментарий

"при выключении BPM'Online" - это очень странная фраза, учитывая, что bpm-серверное приложение и всегда активно. Ну допустим, что все пользователи не будут бездумно жать на крест в браузере(я так понимаю тогда вообще не отследить), а будут разлогиниваться перед закрытием приложения(кто блин вообще так делает в 2018?)

1) logout в MainHeaderSchema в функции onExitMenuItemClick

2) при входе грузится ViewModuleWrapper и ConfigurationViewModule. Я думаю легче перегрузить wrapper(в нем меньше мусора)



Но, опять же, при такой архитектуре всё будет работать в "лабораторных" условиях: пользователь залогинился, НЕ обновляет никакие страницы, (скорее всего) не открывает доп вкладки (иначе происходит загрузка приложения с нуля, отработает теоретический register), перед закрытием браузера выходит из приложения.

Возможно, кто-нибудь из разработчиков подскажет, можно ли с серверной стороны как-то отследить пользователя (ip-шники же пишутся в деталь пользователя)

Показать все комментарии

При работе и выполнении команд WorkspaceConsole.

Возникает ошибка.

Error: Object reference not set to an instance of an object.

Как ее решить?

Нравится

3 комментария

Необходимо проверить настройки подключения в файле Terrasoft.Tools.WorkspaceConsole.exe.config, который находится в той же директории WorkspaceConsole.

•В секции <connectionStrings> укажите параметры подключения, которые используются в файле конфигурации ConnectionStrings.config;

•В секции <db> укажите значение параметра connectionStringName="db".

Если рекомендации не помогут, нужен будет лог запуска консоли, сохраняется в папку WorkspaceConsole\Log 

Мотков Илья,

Аналогичная проблема при запуске команды "Build Workspace" при ведении разработки в файловой системе. 

Лог в папке WorkspaceConsole\Log не создается.

 

Аналогично, проверьте настройки. Без дополнительной информации Вам никто причину не определит.

Показать все комментарии

Добрый день.

Может кто сталкивался с реализацией или знает как сделать:

есть функциональная руль (Роль 1) или организационная роль (Роль 2). В данные роли входят n-количество сотрудников. Есть БП в котором есть активность которую  необходимо распределить на одного из ответственных, например используя функциональную роль. Вопрос: как это реализовано в коробке, есть ли такое распределение?

Нравится

5 комментариев

Добрый день!



Это реализовано в базовой сборке при помощи элемента "Изменить права доступа". При помощи него можно настроить у какого объекта меняются права. А также кому добавить/забрать права на чтение/редактирование/удаление.

Евгений Манько пишет:

Добрый день!

Это реализовано в базовой сборке при помощи элемента "Изменить права доступа". При помощи него можно настроить у какого объекта меняются права. А также кому добавить/забрать права на чтение/редактирование/удаление.

Я имею ввиду не это. А очередь на кого назначить свою задача если например 5 сотрудников свободно

Кулак Евгений Витальевич,

Уточните что Вы имеете ввиду под свободен? У сотрудника из группы меншьше всего активностей в работе? Или нужен не занятый в это время сотрудник?

Евгений Манько,

Кейс заключается в распределении активностей на сотрудников отдела автоматически процессом. Например по заданному алгоритму. Пример работа верификаторов по кредитной заявке. Активности должны распределяться равномерно в соответствии с некой очередью

Кулак Евгений Витальевич,

Вашу задачу можно реализовать при помощи проставления временного признаков до конца обработки задачи. Данный признак будет проставляться для пометки о том, что данный пользователь уже вычитан и процессу не нужно его вычитывать. Схематично бизнес процесс по Вашей задаче можно реализовать так

В подобной реализации есть несколько слабых мест. Для вычитки пользователей с целью присвоения/удаления признака процесс требуется зациклить до окончания присвоения/удаления признака у всей группы пользователей. Данная операция увеличит нагрузку на сервер, а также увеличит время обработки. Так же в случае старта следующего процесса до конца обработки первого может возникнуть конфликт процессов. Процесс – 1 в тот момент будет удалять временный признак, а процесс – 2 добавлять. Или часть пользователей по процессу – 2 не будут считаны так как процесс – 1 уже проставил временный признак. В связи с этим мы рекомендуем использовать Вам элемент «Задание сценарий» в рамках которого Вы сможете реализовать любую необходимую Вам логику.  

 

Показать все комментарии

Добрый день.

Есть скрипт обработки лида. Предположим, вопрос 1, 2, 3. Как только менеджер отмечает ответ клиента на первый вопрос, происходит переход к новому вопросу. Можно ли реализовать иерархию вопросов? Чтобы после выбора ответа на экран ниже выводился новый вопрос, а не происходил переход к новой странице со вторым вопросом

Нравится

1 комментарий

Настроить элемент [Вопрос пользователю] не получится.

Как вариант можно создать преднастроенную страницу и настроить отображение и логику обработки всех последующих вопросов в виде текстовых полей.

Показать все комментарии

11 февраля 2016 года группа компаний Terrasoft представила bpm’online bank customer journey 7.7 — новую версию CRM-платформы для автоматизации задач розничного фронт-офиса банка. В фокусе релиза — технологии для быстрого изменения процессов банка в ответ на трансформацию рынка, а также инструменты для формирования исключительного опыта взаимодействия клиента с банком.
bank
Совсем скоро будет доступна запись вебинара.
А пока узнать о возможностях новой версии можно на сайте.

Нравится

Поделиться

0 комментариев
Показать все комментарии

Коллеги, в разделе Экспертиза вышла новая статья.

Материал «Омниканальный банкинг»
погружает читателя в понятие омниканальности – от её преимуществ для банков и их клиентов до кейсов финансовых организаций, которые уже внедрили омниканальность.

Также в материале проанализированы препятствия, которые мешают банкам перейти из традиционного формата в цифровой (неправильный подход к внедрению омниканальности, зоопарк систем, безопасность, сложности в реализации омниканального обслуживания банками-партнерами) и предложен ряд полезных рекомендаций для их преодоления.

Как повысить эффективность продаж и лояльность клиентов, коммуницируя с ними по наиболее подходящему каналу или их комбинации — узнайте из статьи в разделе Экспертиза.

Нравится

Поделиться

0 комментариев
Показать все комментарии

Алекс Сион, сооснователь нашумевшего стартапа, первого в мире виртуального банка без отделений — выступит в рамках финансовой секции Дня CRM в бизнес-школе СКОЛКОВО!

Вместе с Бреттом Кингом, автором концепций «Банкинг 2.0» и «Банкинг 3.0» Алекс создал первый в мире виртуальный «банк» без отделений. За его плечами 18 лет опыта в банковской сфере и консультирование таких финансовых лидеров как HSBC, Citi, Bank of Montreal, Bank of Scotland и Barclays в сфере диджитал-стратегии и изменений.

Эксперт, голос которого действительно значим в мировой финансовой индустрии, озвучит 5 прогнозов на следующих 5 лет развития банкинга и расскажет, как банкам внедрить омниканальный фронт-офис, который будет соответствовать будущим трансформациям рынка:

  1. Что такое банкинг будущего?
  2. Как изменится ландшафт финансового рынка в результате digital-трансформации?
  3. Эволюционируют ли инструменты для идентификации целевых клиентов банка и их потребностей, а также способы удовлетворения этих потребностей?
  4. Как банку рассказать о бизнес-ценности своих продуктов и услуг, чтобы клиент точно услышал?
  5. Что позволит финансовым организациям быть на шаг впереди своих конкурентов?

Присоединяйтесь к Дню CRM!

Нравится

Поделиться

0 комментариев
Показать все комментарии

Коллеги,

Приглашаем на вебинар «Интеллектуальное управление продажами банковских продуктов»!

В четверг, 3 сентября мы расскажем, как:

  • снизить стоимость привлечения клиентов (САС);
  • повысить доход от всех продуктов по клиенту (LTV);
  • сформировать тактику продажи при привлечении холдингов и ГСК;
  • получить максимальный ROI от работы с CRM-системой.

Присоединяйтесь!

Нравится

Поделиться

0 комментариев
Показать все комментарии