Введение
Как-то в одной из организаций приключилась такая проблема. У диспетчера, в 1С, отчет по заправкам перестал выдавать данные. Над проблемой трудились все, и программисты, и сисадмины. Думали очень долго, думали усердно, почему же данные не поступают, но впоследствии все оказалось намного проще.
Приложение АЗС "Топаз" работает в однопоточном режиме и когда оно запущено у одного пользователя, у другого оно перестает работать, и данные в 1С прекращают поступать. Так, решено было сделать оповещатель-бота, который при отсутствии данных отправлял бы сообщение в telegram об ошибке.
Как сделать проверку
По какому принципу сделать проверку? Было два варианта: проверять соединение к базе данных, и если нет соединения, то нет и данных. И второй вариант, когда данные уже пришли в 1С и нужно проверять их наличие.
Первый вариант не подходит, так как соединение с базой данных может быть, но данных в ней, например нет.
Второй вариант подходит больше, потому что, если соединения нет, мы данных не получим, и таблица будет пустая. Если соединение есть, но данных все равно нет, мы можем проверить, в течение какого времени данные не приходили. И на основании этого сделать выводы о наличии и записи данных.
Написание обработки для проверки
В целом здесь описаны все методы, с помощью которых можно написать бота самостоятельно, но также будет прикреплена внешняя обработка, на случай, если кому-то будет лень писать.
Бот проверки должен выполняться фоново и независимо от программиста. Поэтому мы сделаем команду, которую сможем поместить в регламентное задание.
В "СведенияОВнешнейОбработке", помещаем команду:
СтрокаКоманды = ТЗКоманды.Добавить();
СтрокаКоманды.Представление = "Проверить данные Топаз";
СтрокаКоманды.ПоказыватьОповещение = Ложь;
СтрокаКоманды.Идентификатор = СтрокаКоманды.Представление;
СтрокаКоманды.Использование = "ВызовСерверногоМетода";
Полная функция выглядит так:
Функция СведенияОВнешнейОбработке() Экспорт
сНаименование = "АРМ Загрузка заправок";
сВерсия = "1.1";
РегистрационныеДанные = Новый Структура;
РегистрационныеДанные.Вставить("Вид", "ДополнительнаяОбработка");
РегистрационныеДанные.Вставить("Наименование", сНаименование);
РегистрационныеДанные.Вставить("Версия", сВерсия);
РегистрационныеДанные.Вставить("БезопасныйРежим", Ложь);
РегистрационныеДанные.Вставить("Информация", сНаименование);
ТЗКоманды = Новый ТаблицаЗначений;
ТЗКоманды.Колонки.Добавить("Идентификатор");
ТЗКоманды.Колонки.Добавить("Представление");
ТЗКоманды.Колонки.Добавить("Модификатор");
ТЗКоманды.Колонки.Добавить("ПоказыватьОповещение");
ТЗКоманды.Колонки.Добавить("Использование");
СтрокаКоманды = ТЗКоманды.Добавить();
СтрокаКоманды.Представление = "Проверить данные Топаз";
СтрокаКоманды.ПоказыватьОповещение = Ложь;
СтрокаКоманды.Идентификатор = СтрокаКоманды.Представление;
СтрокаКоманды.Использование = "ВызовСерверногоМетода";
РегистрационныеДанные.Вставить("Команды", ТЗКоманды);
Возврат РегистрационныеДанные;
КонецФункции
Далее экспортная функция для выполнения команды:
Функция ВыполнитьКоманду(ИдентификаторКоманды) Экспорт
Если ИдентификаторКоманды = "Проверить данные Топаз" Тогда
Сообщить("Выполняю Топаз");
ЗаписьЖурналаРегистрации("Выполняюсь Топаз", УровеньЖурналаРегистрации.Информация, Метаданные.ВнешниеИсточникиДанных,,"Топаз",);
ПроверитьЗаписиТопаз();
КонецЕсли;
КонецФункции
Сама функция проверки будет получать данные из АЗС "Топаз", помещать в таблицу значений и проверять, когда были последние данные. Если слишком старые, будем отправлять сообщение, что что-то не так.
Процедура ПроверитьЗаписиТопаз() Экспорт
ПараметрыНастроек = ОбщегоНазначения.ХранилищеНастроекДанныхФормЗагрузить("КПК_уатАРМЗагрузкаЗаправочек", "ПараметрыНастроек", Неопределено,,"ПользовательАРМККЗ");
Если ПараметрыНастроек <> Неопределено Тогда
Организации = ПараметрыНастроек.Организации;
Часы = ПараметрыНастроек.ИнтервалПроверкиВЧасах;
Для каждого Организация Из Организации Цикл
Сообщить(Организация);
ТаблицаТопаз = ПолучитьДанныеПоТопазПроверкаПоследнихЗаправок( Организация );
Сообщить(ТаблицаТопаз[0].ДатаЗаправки);
Сообщить((ТаблицаТопаз.Количество()
И ТаблицаТопаз[0].ДатаЗаправки < НачалоДня(ТекущаяДата() - 60*60*Часы )));
Если //Истина
ТаблицаТопаз = Неопределено
Или (ТаблицаТопаз.Количество()
И ТаблицаТопаз[0].ДатаЗаправки < ТекущаяДата() - 60*60*Часы )
Тогда
СтрокаОтправки = "Топаз " + Организация + " - нет данных за последние "+Часы+" часов. Последние данные от " +ТаблицаТопаз[0].ДатаЗаправки+ ".";
ЕстьУспешнаяОтправка = ОтправкаВТГ(СтрокаОтправки);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Для получения данных из АЗС "Топаз", соединяемся с базой данных строкой:
СтрокаСоединения = "DRIVER=Firebird ODBC Driver;CHARSET=WIN1251;DBNAME=; ROLE=; USER=; PWD=";
Предварительно вставив свои значения для подключения.
Функция для получения данных из АЗС "Топаз" состоит из запроса к базе данных АЗС и заполнения таблицы значений:
Функция ПолучитьДанныеПоТопазПроверкаПоследнихЗаправок(ОрганизацияП = Неопределено) Экспорт
СтрокаСоединения = "DRIVER=Firebird ODBC Driver;CHARSET=WIN1251;DBNAME=; ROLE=; USER=; PWD=";
Если СтрокаСоединения = "" Тогда
Возврат ТЗЗаправки;
КонецЕсли;
Попытка
Connection = Новый COMОбъект("ADODB.Connection");
Connection.Open(СтрокаСоединения);
RS = Новый COMОбъект("ADODB.Recordset");
RS.ActiveConnection = Connection;
ТекстЗапроса = "select FIRST 1
|""rgAmountRests"".""Date"",
|""sysDocTypes"".""Name"" AS DocName,
|""rgAmountRests"".""CardID"" AS CardID,
|""rgAmountRests"".""PartnerID"" AS PartnerID,
|""rgAmountRests"".""Rest"" AS Rest,
|""rgAmountRests"".""Quantity"" AS Quantity,
|""dcCards"".""Name"" AS Name,
|""dcCards"".""Code"" AS Code,
|""dcCards"".""ExtCode"" AS ExtCode,
|""dcCardsExt"".""Name"" AS NameP,
|""dcCardsExt"".""ExtCode"" AS ExtCodeP,
|""dcAmounts"".""Name"" AS GasName
|from
|""rgAmountRests""
| left join ""dcCards"" on ""rgAmountRests"".""CardID"" = ""dcCards"".""CardID"" and ""rgAmountRests"".""PartnerID"" = ""dcCards"".""PartnerID""
| left join ""dcCardsExt"" on ""rgAmountRests"".""CardExtID"" = ""dcCardsExt"".""CardExtID"" and ""rgAmountRests"".""PartnerID"" = ""dcCardsExt"".""PartnerID""
| left join ""sysDocTypes"" on ""rgAmountRests"".""DocTypeID"" = ""sysDocTypes"".""DocTypeID""
| left join ""dcAmounts"" on ""rgAmountRests"".""AmountID"" = ""dcAmounts"".""AmountID""
|
|where
| ""rgAmountRests"".""Quantity"" <> 0
|
| order by
| ""rgAmountRests"".""Date"" DESC";
RS.Open(ТекстЗапроса);
//Перемещаем указатель на первую запись.
RS.MoveFirst();
ТЗЗаправки.Очистить();
Пока RS.EOF() = 0 Цикл
// Обрабатываем значения полей выборки.
НСтрТЗ = ТЗЗаправки.Добавить();
НСтрТЗ.ДатаЗаправки = RS.Fields("Date").Value;
НСтрТЗ.ТипДокумента = RS.Fields("DocName").Value;
Если RS.Fields("Quantity").Value < 0 Тогда
НСтрТЗ.Количество = RS.Fields("Quantity").Value * (-1);
Иначе
НСтрТЗ.Количество = RS.Fields("Quantity").Value;
КонецЕсли;
ФИОВодителя = RS.Fields("NameP").Value;
НСтрТЗ.ВодительФИО = ФИОВодителя;
НСтрТЗ.КодТК = RS.Fields("Code").Value;
НСтрТЗ.Код = RS.Fields("CardID").Value;
RS.MoveNext();
КонецЦикла;
//Закрываем соединения.
RS.Close();
Connection.Close();
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
Возврат ТЗЗаправки;
КонецФункции
После получения таблицы из базы данных АЗС "Топаз", возвращаемся к Процедуре ПроверитьЗаписиТопаз() Экспорт, теперь мы проверяем разность последних полученных данных и текущей даты.
Если //Истина
ТаблицаТопаз = Неопределено
Или (ТаблицаТопаз.Количество()
И ТаблицаТопаз[0].ДатаЗаправки < ТекущаяДата() - 60*60*Часы )
Тогда
СтрокаОтправки = "Топаз " + Организация + " - нет данных за последние "+Часы+" часов. Последние данные от " +ТаблицаТопаз[0].ДатаЗаправки+ ".";
ЕстьУспешнаяОтправка = ОтправкаВТГ(СтрокаОтправки);
КонецЕсли;
И если текущая дата сильно отличается от времени, которое мы указали как параметр, отправляем сообщение в телеграмм.
О телеграмме: так как в последнее время телеграмм блокируют и все переходят на MAX, либо на другие способы передачи сообщений, рассказ будет вкратце, только методы.
Функция ОтправкаВТГ(СтрокаОтправки) Экспорт
Возврат ОтправитьВТГ(ЭтотОбъект, СтрокаОтправки);
КонецФункции // ()
Функция ОтправитьВТГ(Объект, Сообщение)Экспорт
ТаблицаURL = ПолучитьURL(Объект);
МассивКодов = Новый массив;
//МассивОтправленных = Новый Массив;
ЕстьУспешнаяОтправка = Ложь;
Если НЕ ТаблицаURL.Количество() Тогда
Сообщить("Не найдено подходящей рассылки для этого объекта.");
Иначе
Для каждого ЭлементМассиваГрупп Из ТаблицаURL Цикл
URL_Telegram = ЭлементМассиваГрупп.УРЛ;
URL_С_Сообщением = URL_Telegram + Сообщение;
КодСостояния = ОтправитьВTelegram(URL_С_Сообщением);
Если КодСостояния = 200 Тогда
Сообщить("Сообщение отправлено в группу """ + ЭлементМассиваГрупп.Имя + """.");
ЕстьУспешнаяОтправка = Истина;
Иначе
Сообщить("Сообщение не отправлено в группу """ + ЭлементМассиваГрупп.Имя + """, код " + КодСостояния + "." );
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат ЕстьУспешнаяОтправка;
КонецФункции // ()
Функция ПолучитьURL(Объект) Экспорт
ПолучитьУРЛ = Новый Запрос;
ПолучитьУРЛ.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
| ГруппыРассылкиВТГ.URLГруппыВТГ КАК URLГруппыВТГ,
| ГруппыРассылкиВТГ.ИмяРеквизита1 КАК ИмяРеквизита1,
| ГруппыРассылкиВТГ.ЗначениеРеквизита1 КАК ЗначениеРеквизита1,
| ГруппыРассылкиВТГ.ИмяРеквизита2 КАК ИмяРеквизита2,
| ГруппыРассылкиВТГ.ЗначениеРеквизита2 КАК ЗначениеРеквизита2,
| ГруппыРассылкиВТГ.ИмяРеквизита3 КАК ИмяРеквизита3,
| ГруппыРассылкиВТГ.ЗначениеРеквизита3 КАК ЗначениеРеквизита3,
| ГруппыРассылкиВТГ.НаименованиеРассылки КАК НаименованиеРассылки
|ИЗ
| РегистрСведений.ГруппыРассылкиВТГ КАК ГруппыРассылкиВТГ
|ГДЕ
| ГруппыРассылкиВТГ.ТипОбъектаМД = &ТипОбъектаМД";
ПолучитьУРЛ.УстановитьПараметр("ТипОбъектаМД", "Проверить данные Топаз");
ТЗГрупп = ПолучитьУРЛ.Выполнить().Выгрузить();
//ВошелВДокумент = Ложь;
БылаРассылка = Ложь;
ТаблицаURL = Новый ТаблицаЗначений;
ТаблицаURL.Колонки.Добавить("УРЛ");
ТаблицаURL.Колонки.Добавить("Имя");
Для каждого СтрокаТЗГрупп Из ТЗГрупп Цикл
ИмяОбъекта1 = "";
ИмяОбъекта2 = "";
ИмяОбъекта3 = "";
ЗначениеОбъекта1 = Неопределено;
ЗначениеОбъекта2 = Неопределено;
ЗначениеОбъекта3 = Неопределено;
СтрокаURL = ТаблицаURL.Добавить();
СтрокаURL.УРЛ = СтрокаТЗГрупп.URLГруппыВТГ;
СтрокаURL.Имя = СтрокаТЗГрупп.НаименованиеРассылки;
КонецЦикла;
ТаблицаДляОтправки = Новый ТаблицаЗначений;
ТаблицаДляОтправки.Колонки.Добавить("УРЛ");
ТаблицаДляОтправки.Колонки.Добавить("Имя");
ТаблицаURLСвернутая = ТаблицаURL.Скопировать(,"УРЛ");
ТаблицаURLСвернутая.Свернуть("УРЛ");
Для каждого СтрокаТаблицыУрлов Из ТаблицаURLСвернутая Цикл
ФильтрПоТаблице = Новый Структура;
ФильтрПоТаблице.Вставить("УРЛ", СтрокаТаблицыУрлов.УРЛ);
СтрокаДобавления = ТаблицаURL.НайтиСтроки(ФильтрПоТаблице)[0];
СтрокаТабл = ТаблицаДляОтправки.Добавить();
СтрокаТабл.УРЛ = СтрокаДобавления.УРЛ;
СтрокаТабл.Имя = СтрокаДобавления.Имя;
КонецЦикла;
Возврат ТаблицаДляОтправки;
КонецФункции // ()
Функция ОтправитьВTelegram(ПолныйURL)
Попытка
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Ресурс = ПолныйURL;
Соединение = Новый HTTPСоединение("api.telegram.org",443,,,,,ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос(Ресурс);
Ответ = Соединение.Получить(Запрос);
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
Возврат Ответ.КодСостояния;
КонецФункции
Заключение
В целом здесь описаны все методы, с помощью которых можно написать бота самостоятельно, но также будет прикреплена внешняя обработка, на случай, если кому-то будет лень писать, либо не хочется тратить время.
Проверено на следующих конфигурациях и релизах:
- Бухгалтерия предприятия, редакция 3.0, релизы 3.0.194.18
Вступайте в нашу телеграмм-группу Инфостарт