Pull to refresh

Автоматизация sms-рассылок при помощи 1С

CRM systems *ERP-systems *
Awaiting invitation
Многие компании используют в своей деятельности различные сервисы sms-рассылок, для коммуникации со своими клиентами. Зачастую это носит характер выгрузки списка телефонов клиентов из базы и общей рассылки рекламных объявлений по ним. При такой реализации оценить эффективность рассылки сложно, и требуется постоянно актуализировать список клиентов на сайте. Если использовать связку 1С + API sms-шлюза, можно сильно упростить данную задачу, а так же строить гораздо более сложные алгоритмы для рассылки.

Мой пример такой интеграции под катом.

Дано:

Компания занимается розничной торговлей одеждой. Клиентская база довольна постоянна. Управленческий учет ведется в некой конфигурации 1С. В выложенных листингах, код написан для платформы 1С 8.2. Для sms-рассылки используется сторонний веб-сервис с API (Что бы не проводить рекламу, сайт сервиса не указываю). Конфигурация не типовая, однако данный подход, как мне кажется, применим для любой конфигурации, где клиент персонализирован (есть карточка контрагента с контактами).

Задача:

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

Идея реализации:

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

Общее описание технической реализации:

Вся пользовательская работа проходит на стороне 1С:

Пользователь в 1С формирует выборку клиентов по различным признакам, описывает шаблоны текстов, анализирует результаты и инициирует отправку sms-сообщений. 1С подключается к sms-шлюзу через API, и через методы POST/GET выполняет необходимый функционал.

Реализация:

1. Формируем запрос, в котором выбираем клиентскую базу, а так же всю информацию по которой накладывается отбор.

Тут и далее в листингах кода используются регистры нетиповой конфигурации, для Ваших случаев требуется изменить код. Идея в том, что Вам надо выборкой получить всю таблицу, на которую уже затем накладывать отбор, путем программного формирования области запроса «ГДЕ».
На полученную таблицу далее мы будем накладывать различные условия, для получения нужного сегмента клиентов.

Листинг 1. Формируем код запроса
Запрос = Новый Запрос;
	ТекстЗапроса = 
	"ВЫБРАТЬ
	|	ПродажиОбороты.Контрагент КАК Контрагент,
	|	ПродажиОбороты.Контрагент.Телефон КАК Телефон,
	|	ПродажиОбороты.СуммаОборот КАК СуммаОборот,
	|	КонтрагентыАБСКлассификацияСрезПоследних.Параметр КАК АБСКласс,
	|	КонтрагентыXYZКлассификацияСрезПоследних.Параметр КАК XYZКласс,
	|	КонтрагентыЦеновыеГруппыСрезПоследних.Параметр КАК ЦеноваяГруппа,
	|	ПродажиОбороты.КоличествоОборот,
	|	ПродажиОбороты.СуммаСкидкиОборот
	|ПОМЕСТИТЬ ДоктЧ
	|ИЗ
	|	РегистрНакопления.Продажи.Обороты(&НачПериода, &КонПериода, , Контрагент <> &ЧЛ) КАК ПродажиОбороты
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтрагентыАБСКлассификация.СрезПоследних КАК КонтрагентыАБСКлассификацияСрезПоследних
	|		ПО ПродажиОбороты.Контрагент = КонтрагентыАБСКлассификацияСрезПоследних.Контрагент
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтрагентыXYZКлассификация.СрезПоследних КАК КонтрагентыXYZКлассификацияСрезПоследних
	|		ПО ПродажиОбороты.Контрагент = КонтрагентыXYZКлассификацияСрезПоследних.Контрагент
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтрагентыЦеновыеГруппы.СрезПоследних КАК КонтрагентыЦеновыеГруппыСрезПоследних
	|		ПО ПродажиОбороты.Контрагент = КонтрагентыЦеновыеГруппыСрезПоследних.Контрагент
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Контрагент
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	СУММА(1) КАК ПокупокВПериоде,
	|	ПродажиОбороты.Контрагент КАК Контрагент
	|ПОМЕСТИТЬ ДокТЧ2
	|ИЗ
	|	РегистрНакопления.Продажи.Обороты(&НачПериода, &КонПериода, День, Контрагент <> &ЧЛ) КАК ПродажиОбороты
	|
	|СГРУППИРОВАТЬ ПО
	|	ПродажиОбороты.Контрагент
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Контрагент
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ДоктЧ.Контрагент,
	|	ДоктЧ.Телефон,
	|	ДоктЧ.СуммаОборот КАК СуммаОборот,
	|	ДоктЧ.АБСКласс,
	|	ДоктЧ.XYZКласс,
	|	ДоктЧ.ЦеноваяГруппа,
	|	ДокТЧ2.ПокупокВПериоде,
	|	ДоктЧ.КоличествоОборот,
	|	ДоктЧ.СуммаСкидкиОборот,
	|	ДатыПокупокСрезПоследних.Период КАК ДатаПоследнейПокупки,
	|	SMSСообщенияОтправленныеСрезПоследних.КороткийТекстСообщения,
	|	SMSСообщенияОтправленныеСрезПоследних.Период КАК ДатаSMSСообщения
	|ПОМЕСТИТЬ ДокТЧ4
	|ИЗ
	|	ДоктЧ КАК ДоктЧ
	|		ЛЕВОЕ СОЕДИНЕНИЕ ДокТЧ2 КАК ДокТЧ2
	|		ПО ДоктЧ.Контрагент = ДокТЧ2.Контрагент
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДатыПокупок.СрезПоследних КАК ДатыПокупокСрезПоследних
	|		ПО ДоктЧ.Контрагент = ДатыПокупокСрезПоследних.Контрагент
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.SMSСообщенияОтправленные.СрезПоследних КАК SMSСообщенияОтправленныеСрезПоследних
	|		ПО ДоктЧ.Контрагент = SMSСообщенияОтправленныеСрезПоследних.Контрагент
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ДокТЧ4.Контрагент,
	|	ДокТЧ4.Телефон,
	|	ДокТЧ4.СуммаОборот КАК СуммаОборот,
	|	ДокТЧ4.АБСКласс,
	|	ДокТЧ4.XYZКласс,
	|	ДокТЧ4.ЦеноваяГруппа,
	|	ДокТЧ4.ПокупокВПериоде,
	|	ДокТЧ4.КоличествоОборот,
	|	ДокТЧ4.СуммаСкидкиОборот,
	|	ДокТЧ4.ДатаПоследнейПокупки,
	|	РАЗНОСТЬДАТ(ДокТЧ4.ДатаПоследнейПокупки, &ТекДата, ДЕНЬ) КАК ДнейСПоследнейПокупки,
	|	ДокТЧ4.КороткийТекстСообщения,
	|	ДокТЧ4.ДатаSMSСообщения,
	|	РАЗНОСТЬДАТ(ДокТЧ4.ДатаSMSСообщения, &ТекДата, ДЕНЬ) КАК ДнейСПоследнегоSMSСообщения,
	|	ВЫБОР
	|		КОГДА ДокТЧ4.СуммаСкидкиОборот + ДокТЧ4.СуммаОборот <> 0
	|			ТОГДА ДокТЧ4.СуммаСкидкиОборот / (ДокТЧ4.СуммаСкидкиОборот + ДокТЧ4.СуммаОборот) * 100
	|		ИНАЧЕ 0
	|	КОНЕЦ КАК ПроцентСкидки,
	|	ВЫБОР
	|		КОГДА ДокТЧ4.ПокупокВПериоде <> 0
	|			ТОГДА ДокТЧ4.СуммаОборот / ДокТЧ4.ПокупокВПериоде
	|		ИНАЧЕ 0
	|	КОНЕЦ КАК СреднийЧек,
	|	ВЫБОР
	|		КОГДА ДокТЧ4.КоличествоОборот <> 0
	|			ТОГДА ДокТЧ4.СуммаОборот / ДокТЧ4.КоличествоОборот
	|		ИНАЧЕ 0
	|	КОНЕЦ КАК СредняяСтоимостьВещи
	|ПОМЕСТИТЬ ДокТЧ5
	|ИЗ
	|	ДокТЧ4 КАК ДокТЧ4
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ДокТЧ5.Контрагент,
	|	ДокТЧ5.Телефон,
	|	ДокТЧ5.СуммаОборот КАК СуммаОборот,
	|	ДокТЧ5.АБСКласс,
	|	ДокТЧ5.XYZКласс,
	|	ДокТЧ5.ЦеноваяГруппа,
	|	ДокТЧ5.ПокупокВПериоде,
	|	ДокТЧ5.КоличествоОборот,
	|	ДокТЧ5.СуммаСкидкиОборот,
	|	ДокТЧ5.ДатаПоследнейПокупки,
	|	ДокТЧ5.ДнейСПоследнейПокупки,
	|	ДокТЧ5.КороткийТекстСообщения,
	|	ДокТЧ5.ДатаSMSСообщения,
	|	ЕстьNULL(ДокТЧ5.ДнейСПоследнегоSMSСообщения, 0) КАК ДнейСПоследнегоSMSСообщения,
	|	ДокТЧ5.ПроцентСкидки,
	|	ДокТЧ5.СреднийЧек,
	|	ДокТЧ5.СредняяСтоимостьВещи
	|ИЗ
	|	ДокТЧ5 КАК ДокТЧ5
	|ГДЕ
	|	ДокТЧ5.СуммаОборот МЕЖДУ &СуммаНач И &СуммаКон";


2. В зависимости от выставленных пользователем фильтров программно формируем условия запроса.

Листинг 2. Программное формирование раздела условий в запросе
//Процедура формирует текст подвала запроса, где описывается условие где
//
Процедура СформироватьПодвалЗапроса(ТекстЗапроса)
	
	// Фильтр по АБС
	Если ФильтрАБС <> Перечисления.ABCКлассификация.ПустаяСсылка() Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	И ДокТЧ5.АБСКласс = &АБСКласс"
	КонецЕсли;
	
	// Фильтр по XYZ
	Если ФильтрXYZ <> Перечисления.XYZКлассы.ПустаяСсылка() Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	И ДокТЧ5.XYZКласс = &XYZКласс"
	КонецЕсли;
	
	// Фильтр по Ценовой группе
	Если ФильтрЦеноваяГруппа <> Перечисления.ЦеноваяГруппа.ПустаяСсылка() Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|	И ДокТЧ5.ЦеноваяГруппа = &ЦеноваяГруппа"
	КонецЕсли;
	
	//Фильтр пустых контактов
	Если ФильтрОтображатьКлиентовБезТелефонов = Ложь Тогда
		ТекстЗапроса = ТекстЗапроса + "	
		|	И ДокТЧ5.Телефон <> """"
		|	И ДокТЧ5.Телефон <> &ПустойТелефон"
	КонецЕсли;
	
	// Фильтр по кол-ву покупок в периоде, кол-ву оборот, дней с последнего sms, проценту скидки, средней стоимости
	ТекстЗапроса = ТекстЗапроса + "	
	|	И ДокТЧ5.ПокупокВПериоде МЕЖДУ &ПокупокВПериодеНач И &ПокупокВПериодеКон
	|	И ДокТЧ5.КоличествоОборот МЕЖДУ &КоличествоНач И &КоличествоКон	
	|	И ДокТЧ5.ПроцентСкидки МЕЖДУ &ПроцентСкидкиНач И &ПроцентСкидкиКон
	|	И ДокТЧ5.СреднийЧек МЕЖДУ &СреднийЧекНач И &СреднийЧекКон
	|	И ДокТЧ5.СредняяСтоимостьВещи МЕЖДУ &СредняяСтоимостьВещиНач И &СредняяСтоимостьВещиКон
	|";	
	
КонецПроцедуры



3. Устанавливаем параметры запроса.

В результате в выборке остается только нужный сегмент клиентов.

Листинг 3. Установка параметров запроса
//Процедура устанавливает параметры запроса на основани фильтров
//
Процедура УстановитьПараметрыЗапроса(Запрос)
	
	//Отбор по дате покупки
	НачПериода = ТекущаяДата() - ИнтервалДнейНач*24*60*60;
	НачПериода = НачалоДня(НачПериода);
	
	Если ИнтервалДнейКон <> 0 Тогда
		КонПериода = ТекущаяДата() - ИнтервалДнейКон*24*60*60
	Иначе
		КонПериода = ТекущаяДата()
	КонецЕсли;
		
	КонПериода = КонецДня(КонПериода);

	Запрос.УстановитьПараметр("НачПериода", НачПериода);
	Запрос.УстановитьПараметр("КонПериода", КонПериода);
	
	Запрос.УстановитьПараметр("ТекДата", ТекущаяДата());
	Запрос.УстановитьПараметр("ЧЛ", Константы.ОсновнойПокупатель.Получить());
	
	//Отбор по сумме
	Запрос.УстановитьПараметр("СуммаНач", ИнтервалСуммаНач);
	
	Если ИнтервалСуммаКон = 0 Тогда
		Запрос.УстановитьПараметр("СуммаКон", 99999999999)
	Иначе
		Запрос.УстановитьПараметр("СуммаКон", ИнтервалСуммаКон)
	КонецЕсли;		
	
	//Отбор по АБС классу	
	Если ФильтрАБС <> Перечисления.ABCКлассификация.ПустаяСсылка() Тогда
		Запрос.УстановитьПараметр("АБСКласс", ФильтрАБС)
	КонецЕсли;
	
	// Фильтр по XYZ
	Если ФильтрXYZ <> Перечисления.XYZКлассы.ПустаяСсылка() Тогда		
		Запрос.УстановитьПараметр("XYZКласс", ФильтрXYZ)
	КонецЕсли;
	
	// Фильтр по Ценовой группе
	Если ФильтрЦеноваяГруппа <> Перечисления.ЦеноваяГруппа.ПустаяСсылка() Тогда		
		Запрос.УстановитьПараметр("ЦеноваяГруппа", ФильтрЦеноваяГруппа)
	КонецЕсли;
	
	//По кол-ву покупок
	Запрос.УстановитьПараметр("ПокупокВПериодеНач", ПокупокВПериодеНач);
	
	Если ПокупокВПериодеКон = 0 Тогда                                       		
		Запрос.УстановитьПараметр("ПокупокВПериодеКон", 999999)
	Иначе
		Запрос.УстановитьПараметр("ПокупокВПериодеКон", ПокупокВПериодеКон)
	КонецЕсли;
	
	//По кол-ву вещей
	Запрос.УстановитьПараметр("КоличествоНач", КолВещейНач);
	
	Если КолВещейКон = 0 Тогда                                       		
		Запрос.УстановитьПараметр("КоличествоКон", 999999)
	Иначе
		Запрос.УстановитьПараметр("КоличествоКон", КолВещейКон)
	КонецЕсли;
	
	//По ДнейСПоследнегоSMSСообщения
		
	//По ПроцентСкидки
	Если ПроцентСкидкиНач = 0 Тогда
		Запрос.УстановитьПараметр("ПроцентСкидкиНач", -99999999)
	Иначе 
		Запрос.УстановитьПараметр("ПроцентСкидкиНач", ПроцентСкидкиНач)
	КонецЕсли;
	
	Если ПроцентСкидкиКон = 0 Тогда                                       		
		Запрос.УстановитьПараметр("ПроцентСкидкиКон", 999999)
	Иначе
		Запрос.УстановитьПараметр("ПроцентСкидкиКон", ПроцентСкидкиКон)
	КонецЕсли;	
	
	//По срднему чеку
	Запрос.УстановитьПараметр("СреднийЧекНач", СреднийЧекНач);
	
	Если СреднийЧекКон = 0 Тогда                                       		
		Запрос.УстановитьПараметр("СреднийЧекКон", 999999)
	Иначе
		Запрос.УстановитьПараметр("СреднийЧекКон", СреднийЧекКон)
	КонецЕсли;	
	
	//По средней стоимости вещи
	Запрос.УстановитьПараметр("СредняяСтоимостьВещиНач", СредняяСтоимостьВещиНач);
	
	Если СредняяСтоимостьВещиКон = 0 Тогда                                       		
		Запрос.УстановитьПараметр("СредняяСтоимостьВещиКон", 999999)
	Иначе
		Запрос.УстановитьПараметр("СредняяСтоимостьВещиКон", СредняяСтоимостьВещиКон)
	КонецЕсли;	
	
	//Фильтр пустых контактов
	Если ФильтрОтображатьКлиентовБезТелефонов = Ложь Тогда
		Запрос.УстановитьПараметр("ПустойТелефон", "+7(   )   -  -")
	КонецЕсли;
				
КонецПроцедуры



4. Решаем интерфейсные задачи

В нашем случае форма разбита на три области: область отборов, область текста и информации и область выборки клиентов. Пользователь получает возможность быстро отбирать нужный сегмент, описывать шаблон рассылки и тут же анализировать результат.

Интерфейс пользователя




5. Интеграция с sms-шлюзом через API

Сформировав выборку клиентов и описав шаблон текстов, инициируем передачу сообщений клиенту через веб-сервис (не указываю ссылок сервиса в конкретной реализации, что бы не давать рекламу. Сервисов sms-рассылок довольно много, и многие поддерживают работу через API. Поэтому при использовании данного кода, требуется заменить адрес сайта и метод под конкретный сервис. На стороне 1С логика останется той же).

Итак, в общем модуле мы создаем модуль работы с SMS, и описываем в нем экспортные функции.

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

Листинг 5. Функции общего модуля работы с SMS
//Функция отправляет запрос на портал XXXXXXXXXXX
//		В Случае успеха функция возвращает ответ ИСТИНА, в противном случае - ЛОЖЬ
Функция ОтправкаЗапросаНаПорталSMSSimple(Контрагент, НаНомер, ТекстОтправления, SMSШлюз = Неопределено, СНомера = "") Экспорт	
	
	//По умолчанию
	Если СНомера = "" Тогда
		СНомера = Константы.SMSSimleКодПодписи.Получить()
	КонецЕсли;
	
	Если SMSШлюз = Неопределено Тогда
		SMSШлюз = Константы.ОсновнойSMSШлюз.Получить()
	КонецЕсли;		
	
	//Обработка значений
	ТекстОтправленияОбработанный = СтрЗаменить(ТекстОтправления, """", " ");
	
	//оставляем в номере цифры
	НомерОбработанный = ОставитьТолькоЦифры(НаНомер);
	НомерОбработанный = ПровестиЗаменуВосьмеркиНаСемерку(НомерОбработанный);
	
	Логин = Константы.ЛогинSMSSimple.Получить();
	Пароль = Константы.ПарольSMSSimple.Получить();
		
	//Готовим POST запрос
	Сервер = "www.XXXXXXXX.ru";	
	Ресурс = "http_send.php?user=" + Логин + "&pass=" + Пароль + "&or_id=" + СНомера + "&phone=" + НомерОбработанный + "&message=" + ТекстОтправленияОбработанный;
	
	//Отправляем POST-запрос
	HTTPЗапрос = Новый HTTPЗапрос(Ресурс);
	HTTPСоединение = Новый HTTPСоединение(Сервер);
	ФлагУспех = Истина;
	
	Попытка
		Ответ = HTTPСоединение.Получить(HTTPЗапрос);
	Исключение
		Сообщить("Ошибка при отправке запроса: " + ОписаниеОшибки() + Символы.ПС + "Проверте наличие интернета.");
		ФлагУспех = Ложь;
		Возврат ФлагУспех
	КонецПопытки;
	
	СтрокаОтвет = "";
	Если Ответ.КодСостояния = 200 Тогда
		СтрокаОтвет = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
        		
		Если ПустаяСтрока(СтрокаОтвет) Тогда		
			Сообщить("Сервер " + Сервер + " не отвечает");	
			ФлагУспех = Ложь
		КонецЕсли;
		
	Иначе
		
		Сообщить("Не известный статус ответа сервера. Обратитесь в службу поддержки.");
		ФлагУспех = Ложь
				
	КонецЕсли;
	
	HTTPЗапрос= Неопределено;
	HTTPСоединение = Неопределено;
	
	//Добавим инфу в регист
	Если ФлагУспех = Истина Тогда
		Движение = РегистрыСведений.SMSСообщенияОтправленные.СоздатьМенеджерЗаписи();
		
		Движение.Период = ТекущаяДата();
		Движение.Контрагент = Контрагент;
		ИДСообщения = Константы.ИДОтправления.Получить() + 1;
		Движение.ИДСообщения = ИДСообщения;
		Движение.ТекстСообщения = ТекстОтправленияОбработанный;
		Движение.СПодписи = СНомера;
		Движение.НаНомер = НомерОбработанный;
		Движение.ЧерезSMSШлюз = SMSШлюз;
		Движение.КороткийТекстСообщения = ТекстОтправленияОбработанный;
		
		Попытка
			Движение.Записать()
		Исключение
			Попытка
				Движение.Записать()
			Исключение				
				Предупреждение("Внимание! Произошла исключительная ситуация. Обратитесь в службу поддержки!" + Символы.ПС + ОписаниеОшибки());
				ФлагУспех = Ложь
			КонецПопытки;
		КонецПопытки;		
		
		//Сформируем новый ИД
		Константы.ИДОтправления.Установить(ИДСообщения);
		
	КонецЕсли;       		
	
	//Вернем ответ
	Возврат ФлагУспех

КонецФункции



Листинг 6. Вызов функций общего модуля из обработки формирования рассылки
//Процедура выполняет рассылку сообщений через sms-шлюз
//
Процедура ВыполнитьSMSрассылку() Экспорт
	
	Для Каждого СтрокаТаблица Из ТЧОснование Цикл
		Если СтрокаТаблица.Выбрать = Истина Тогда
			Если СтрокаТаблица.НовоеSMSСообщение = "" Тогда
				Возврат
			КонецЕсли;
			
			//Проверка на сегодняшнюю отправку
			Если НачалоДня(СтрокаТаблица.ДатаПоследнейSMS) = НачалоДня(ТекущаяДата()) Тогда
				Продолжить;				
			КонецЕсли;
			
			Если РаботаСSMS.ОтправкаЗапросаНаПорталSMSSimple(СтрокаТаблица.Контрагент, СтрокаТаблица.Телефон, СтрокаТаблица.НовоеSMSСообщение) = Ложь Тогда
				Предупреждение("Произошла ошибка при отправке. Отправка прервана.");
				Возврат
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	СформироватьТаблицу();			
	
КонецПроцедуры


После отправки сообщения, выборка формируется заново, и клиенты которые получили сообщения пропадают. В итоге мы «режем» всю клиентскую массу небольшими сегментами (от самых «интересных» клиентов к менее «интересным»), пока не проведем рассылку всем или пока оставшаяся выборка не станет «не интересной» для рассылки.

6. Итоги.

Генерация выборки для рассылке на стороне 1С, позволяет не только упростить и удешевить проведение этой рассылки, но и дает возможность учитывать самые разные параметры для формирования более адресных рекламных сообщений. Можно учесть товарные предпочтения клиента по продукции, его особенности по периодам покупок (например клиент приобретает только зимние вещи), влияние на него скидочных программ и прочее. Соблюдая принцип «Выбрали сегмент -> отправили сообщения -> Клиенты пропали из выборки -> Выбрали новый сегмент», можно существенно повысить полезность таких рассылок для клиента и, как результат, конверсию.

P.S.

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

Все рекомендации
— Номера телефонов обычно не имеют общей маски, лучше предусмотреть программное приведение номера к единому шаблона.
Листинг 7. Работа с форматом номера
//Функция меняет восьмерку на семерку
//
Функция ПровестиЗаменуВосьмеркиНаСемерку(НомерТелефона)
		
	Если Лев(НомерТелефона, 1) <> "8" Тогда
		Возврат НомерТелефона
	КонецЕсли;
	
	КолЦифр = СтрДлина(НомерТелефона) - 1;
	
	НовыйНомер = "7" + Прав(НомерТелефона,  КолЦифр);
	
	Возврат НовыйНомер
	
КонецФункции

//Функция оставляет только цифры
//
Функция ОставитьТолькоЦифры(Номер) Экспорт
	
	ДлинаСтроки = СтрДлина(Номер);	
		
	//Уберем пробелы
	ОбработанныйНомер = "";	
	Сч = 1;
	Пока Сч <= СтрДлина(Номер) Цикл
		Если (Сред(Номер, Сч, 1) = "1") Или (Сред(Номер, Сч, 1) = "2") Или (Сред(Номер, Сч, 1) = "3") Или (Сред(Номер, Сч, 1) = "4")
			Или (Сред(Номер, Сч, 1) = "5") Или (Сред(Номер, Сч, 1) = "6") Или (Сред(Номер, Сч, 1) = "7") Или (Сред(Номер, Сч, 1) = "8")
			Или (Сред(Номер, Сч, 1) = "9") Или (Сред(Номер, Сч, 1) = "0") Тогда
				ОбработанныйНомер = ОбработанныйНомер + Сред(Номер, Сч, 1)
		КонецЕсли;
		Сч = Сч + 1
	КонецЦикла;	
	
	Возврат ОбработанныйНомер
	
КонецФункции





— Выборку клиентов лучше формировать заново каждый раз, когда клиент интерактивно меняет параметры фильтров. Это не приводит к критическому повышению нагрузки (т.к. вся выборка формируется единым запросом и к регистрам, которые не могут активно быть задействованы в блокировках), однако сильно улучшает юзабилити для пользователя. Для этого достаточно в модуле формы создать процедуру, которая будет формировать выборку и обновлять информационные надписи, и указать эту процедуру в предопределенной процедуре «При изменении» всех элементов/фильтров пользовательского интерфейса.

Листинг 8. Обновление интерфейса
//Процедура обновляет выборку и интерфейс
//
Процедура ФормированиеТаблицы()
	
	СформироватьТаблицу(); //Экспортная модульная процедура. Код в Листинге 1
	СформироватьИнформационныеНадписи();
	
КонецПроцедуры

//Процедура фирмирует все инфо-надписи
//
Процедура СформироватьИнформационныеНадписи() Экспорт
		
	//Надписи интервала
	НачПериода = ТекущаяДата() - ИнтервалДнейНач*24*60*60;
	
	Если ИнтервалДнейКон <> 0 Тогда
		КонПериода = ТекущаяДата() - ИнтервалДнейКон*24*60*60
	Иначе
		КонПериода = ТекущаяДата()
	КонецЕсли;

	Если НачалоДня(НачПериода) = НачалоДня(ТекущаяДата()) Тогда
		ЭлементыФормы.инфНадписьПериодНач.Заголовок = "Сегодня"
	Иначе
		ЭлементыФормы.инфНадписьПериодНач.Заголовок = Формат(НачПериода, "ДФ='дд МММ гггг'")
	КонецЕсли;
	
	Если НачалоДня(КонПериода) = НачалоДня(ТекущаяДата()) Тогда
		ЭлементыФормы.инфНадписьПериодКон.Заголовок = "Сегодня"
	Иначе
		ЭлементыФормы.инфНадписьПериодКон.Заголовок = Формат(КонПериода, "ДФ='дд МММ гггг'")
	КонецЕсли;	
	
	//Надписи фильтр по дате рассылки
	Если ДнейСПоследнегоSMSСообщенияКон <> 0 Тогда
		ЭлементыФормы.инфДнейСПоследнегоSMSСообщения.Заголовок = "Прошло > " + ДнейСПоследнегоSMSСообщенияКон + " дн. с последней рассылки"
	Иначе
		ЭлементыФормы.инфДнейСПоследнегоSMSСообщения.Заголовок = ""		
	КонецЕсли;
	
	//всего клиентов
	ЭлементыФормы.инфНадписьНадписьКлиентов.Заголовок = "Клиентов: ";
	ЭлементыФормы.инфНадписьВсегоКлиентов.Заголовок = Строка(ВсегоКлиентовВВыборке) + " чел.";
	
	//Доля клиентов
	ЭлементыФормы.инфНадписьДоляКлиентов.Заголовок = "";
	Если ВсегоКлиентовБезФильтровЗаПериод <> 0 Тогда
		ЭлементыФормы.инфНадписьДоляКлиентов.Заголовок = "Сегмент: " + Окр(ВсегоКлиентовВВыборке/ВсегоКлиентовБезФильтровЗаПериод * 100, 0) + "%"
	КонецЕсли;		
	
	//Надпись длинна СМС
	ЭлементыФормы.инфНадписьВсегоSMS.Заголовок = "";
	ЭлементыФормы.инфНадписьСтоимостьSMS.Заголовок = "";
	Если ШаблонSMSРассылкиКопия <> "" Тогда
		ЦелыхСообщений = Цел(мСимволовВSMS/70);
		ЭлементыФормы.инфНадписьКолСимволовВSMS.Заголовок = Строка(мСимволовВSMS) + " симв.";
		
		СимволовОсталось = (ЦелыхСообщений+1)*70-мСимволовВSMS;
		Если мСимволовВSMS - ЦелыхСообщений*70 = 0 Тогда
			ЭлементыФормы.инфНадписьSMS.Заголовок = "SMS: " + ЦелыхСообщений + " шт. "
		Иначе
			ЭлементыФормы.инфНадписьSMS.Заголовок = "SMS: " + (ЦелыхСообщений+1) + " (+" + СимволовОсталось + ")"
		КонецЕсли;
		
		Если СимволовОсталось > 15 Тогда
			ЭлементыФормы.инфНадписьSMS.ЦветТекста = WebЦвета.Зеленый
		ИначеЕсли СимволовОсталось > 5 И СимволовОсталось <=15 Тогда 
			ЭлементыФормы.инфНадписьSMS.ЦветТекста = WebЦвета.Оранжевый
		иначеЕсли СимволовОсталось < 5 Тогда
			ЭлементыФормы.инфНадписьSMS.ЦветТекста = ЦветаСтиля.ЦветОтрицательногоЧисла
		КонецЕсли;			
		
		ЭлементыФормы.инфНадписьНадписьВсегоSMS.Заголовок = "Всего SMS:";
		ЭлементыФормы.инфНадписьВсегоSMS.Заголовок = Строка(ВсегоSMSСообщений) + " шт.";
		ЭлементыФормы.инфНадписьНадписьСтоимостьSMS.Заголовок = "Стоимость:";
		ЭлементыФормы.инфНадписьСтоимостьSMS.Заголовок = Строка(Окр(ВсегоSMSСообщений*1.3, 0)) + " руб." 
		
	КонецЕсли;
	
	//Надпись проверка баланса
	Если СуммаБалансаДляРассылки <> 0 Тогда
		Если СуммаБалансаДляРассылки < Окр(ВсегоSMSСообщений*1.3, 0) Тогда
			ЭлементыФормы.инфНадписьБалансОшибка.Заголовок = "Средств на балансе недостаточно!"
		Иначе
			ЭлементыФормы.инфНадписьБалансОшибка.Заголовок = ""			
		КонецЕсли;
	Иначе
		ЭлементыФормы.инфНадписьБалансОшибка.Заголовок = ""
	КонецЕсли;		
	
КонецПроцедуры


— Перед отправкой сообщений на sms-шлюз, лучше проверить достаточность баланса. Для этого достаточно перед отправкой посчитать количество sms-сообщений, умножить на максимальную цену одного сообщения и сравнить с балансом на счете. Сам балан можно так же получить GET-запросом.

— Что бы при наборе шаблона текста пользователем, автоматически отображался текст для каждого клиента в выборке, можно воспользоваться предопределенной процедурой «АвтоПодборТекста». Однако при том, что это сильно улучшает визуальность для пользователя, это значительно повышает нагрузку на сервер. Тут стоит принимать решения отталкиваясь от возможностей железа/сетевого канала.

Листинг 9. Обработка ввода шаблона текста рассылки
Процедура ШаблонSMSРассылкиАвтоПодборТекста(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)
	
	ШаблонSMSРассылкиКопия = Текст;
	СформироватьНадписьSMSСообщение()
	
КонецПроцедуры

//Процедура считает кол-во символов в набранном sms сообщении
//
Процедура СформироватьНадписьSMSСообщение() Экспорт
	
	Если СтрЧислоВхождений(ШаблонSMSРассылкиКопия, "{Имя}") > 0 Тогда
		мСимволовВSMS = СтрДлина(ШаблонSMSРассылкиКопия)+СтрЧислоВхождений(ШаблонSMSРассылкиКопия, "{Имя}")*4
	Иначе
		мСимволовВSMS = СтрДлина(ШаблонSMSРассылкиКопия)
	КонецЕсли;
		
	ЭлементыФормы.ТЧОснование.Колонки.НовоеSMSСообщение.Видимость = Истина;
	ЭлементыФормы.ТЧОснование.Колонки.КоличествоSMS.Видимость = Истина;
	
	СформироватьИндивидуальныеТекстыSMS();
	
	СформироватьИнформационныеНадписи();
	
КонецПроцедуры


//Процедура формирует индивидуальные SMS сообщения
//
Процедура СформироватьИндивидуальныеТекстыSMS() Экспорт
	
	ВсегоSMSСообщений = 0;	
	Для Каждого СтрокаТаблица Из ТЧОснование Цикл
		Если СтрокаТаблица.Выбрать = Истина Тогда
			СтрокаТаблица.НовоеSMSСообщение = ШаблонSMSРассылкиКопия;
			СтрокаТаблица.НовоеSMSСообщение = СтрЗаменить(СтрокаТаблица.НовоеSMSСообщение, "{Имя}", СтрокаТаблица.Контрагент.Имя);
			КолSMSВрем = Цел(СтрДлина(СтрокаТаблица.НовоеSMSСообщение)/70);
			Если (КолSMSВрем*70 - СтрДлина(СтрокаТаблица.НовоеSMSСообщение)) <> 0 Тогда
				СтрокаТаблица.КоличествоSMS = КолSMSВрем + 1
			Иначе
				СтрокаТаблица.КоличествоSMS = КолSMSВрем
			КонецЕсли;			
			
			ВсегоSMSСообщений = ВсегоSMSСообщений + СтрокаТаблица.КоличествоSMS
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры



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

Листинг 10. Обработка случайного нажатия
//Защита от случайного нажатия
	ГСЧ = Новый ГенераторСлучайныхЧисел;
	Число1 = ГСЧ.СлучайноеЧисло(1, 10);
	Число2 = ГСЧ.СлучайноеЧисло(1, 10);
	
	Ответ = "";
	ВвестиЧисло(Ответ, "Решите задачу. " + Число1 + " + " + Число2 + " = ?");
	
	Если Ответ <> (Число1 + Число2) Тогда
		Предупреждение("Отправка рассылки не произведена. Учим математику...");
		Возврат
	КонецЕсли;


Tags: автоматизация предприятийинтеграция с 1сapi
Hubs: CRM systemsERP-systems
You can’t comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.