Загрузка данных
// Обработчик и локальные помощники для постраничной выгрузки сообщений всех обращений.
// В существующем модуле должны быть доступны процедуры обработки вложений:
// ДобавитьКартинкиФорматированногоКомментария и
// ПодготовитьТекстПисьмаИСтруктуруКартинок.
// Возвращает сообщения обращений, появившиеся не раньше changedSince.
//
// Параметры:
// ЗапросПарам - HTTPСервисЗапрос - первый запрос содержит changedSince и необязательный
// limit; следующие страницы запрашиваются только с параметром cursor.
//
// Возвращаемое значение:
// HTTPСервисОтвет - объект {"messages": [...]}. Если существует следующая страница,
// ответ содержит заголовок X-Next-Cursor.
Функция GetListComments(ЗапросПарам) Экспорт
Перем ТекстОшибкиПараметров;
Попытка
ТекстОшибкиПараметров = "";
Настройки = ПолучитьНастройкиЗапросаКомментариев(ЗапросПарам, ТекстОшибкиПараметров);
Если Настройки = Неопределено Тогда
Возврат ПолучитьОтветJSONКомментариев(400,
Новый Структура("isError,ErrorText", Истина, ТекстОшибкиПараметров));
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = ПолучитьТекстЗапросаКомментариев(Настройки);
Запрос.УстановитьПараметр("ДатаИзменения", Настройки.ДатаИзменения);
Если Настройки.ЕстьГраница Тогда
Запрос.УстановитьПараметр("ПоследняяДата", Настройки.ПоследняяДата);
Запрос.УстановитьПараметр("ПоследнийИсточник", Настройки.ПоследнийИсточник);
Запрос.УстановитьПараметр("ПоследнееОбращение", Настройки.ПоследнееОбращение);
Запрос.УстановитьПараметр("ПоследнийВидПодчиненного", Настройки.ПоследнийВидПодчиненного);
Запрос.УстановитьПараметр("ПоследнийПодчиненныйИнцидент",
Настройки.ПоследнийПодчиненныйИнцидент);
Запрос.УстановитьПараметр("ПоследнийПодчиненныйНаряд", Настройки.ПоследнийПодчиненныйНаряд);
Запрос.УстановитьПараметр("ПоследнийUUID", Настройки.ПоследнийUUID);
Запрос.УстановитьПараметр("ПоследнееВходящееПисьмо", Настройки.ПоследнееВходящееПисьмо);
КонецЕсли;
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Сообщения = Новый Массив;
ЕстьСледующаяСтраница = Ложь;
ПоследнийКлюч = Неопределено;
Пока Выборка.Следующий() Цикл
Если Сообщения.Количество() >= Настройки.Лимит Тогда
ЕстьСледующаяСтраница = Истина;
Прервать;
КонецЕсли;
Сообщения.Добавить(СформироватьСообщениеКомментариев(Выборка));
ПоследнийКлюч = ПолучитьКлючВыборкиКомментариев(Выборка);
КонецЦикла;
ТелоОтвета = Новый Структура;
ТелоОтвета.Вставить("messages", Сообщения);
Ответ = ПолучитьОтветJSONКомментариев(200, ТелоОтвета);
Если ЕстьСледующаяСтраница Тогда
Ответ.Заголовки.Вставить("X-Next-Cursor",
СформироватьКурсорКомментариев(Настройки, ПоследнийКлюч));
КонецЕсли;
Возврат Ответ;
Исключение
Возврат ПолучитьОтветJSONКомментариев(500,
Новый Структура("isError,ErrorText", Истина, "Не удалось обработать запрос"));
КонецПопытки;
КонецФункции
// Возвращает и проверяет параметры первой или следующей страницы.
Функция ПолучитьНастройкиЗапросаКомментариев(ЗапросПарам, ТекстОшибки)
Перем Лимит, ТекстОшибкиДаты;
ТекстОшибки = "";
ПараметрыЗапроса = ЗапросПарам.ПараметрыЗапроса;
Если ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, "cursor") Тогда
СтрокаКурсора = ПараметрыЗапроса.Получить("cursor");
Если СтрокаКурсора = Неопределено Или СтрокаКурсора = "" Тогда
ТекстОшибки = "Параметр cursor не должен быть пустым";
Возврат Неопределено;
КонецЕсли;
Если ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, "changedSince")
Или ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, "limit") Тогда
ТекстОшибки = "Параметр cursor нельзя передавать вместе с changedSince или limit";
Возврат Неопределено;
КонецЕсли;
Возврат РазобратьКурсорКомментариев(СтрокаКурсора, ТекстОшибки);
КонецЕсли;
Если Не ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, "changedSince") Тогда
ТекстОшибки = "Параметр changedSince обязателен";
Возврат Неопределено;
КонецЕсли;
ИзмененоПослеСтрокой = ПараметрыЗапроса.Получить("changedSince");
ТекстОшибкиДаты = "";
РезультатРазбораДаты = РазобратьДатуISOКомментариев(ИзмененоПослеСтрокой, ТекстОшибкиДаты);
Если РезультатРазбораДаты = Неопределено Тогда
ТекстОшибки = ТекстОшибкиДаты;
Возврат Неопределено;
КонецЕсли;
Если ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, "limit") Тогда
СтрокаЛимита = ПараметрыЗапроса.Получить("limit");
Если Не ПолучитьЛимитСтраницыКомментариев(СтрокаЛимита, Лимит) Тогда
ТекстОшибки = "Параметр limit должен быть целым числом от 1 до 500";
Возврат Неопределено;
КонецЕсли;
Иначе
Лимит = 100;
КонецЕсли;
Настройки = Новый Структура;
Настройки.Вставить("ДатаИзменения", РезультатРазбораДаты.Дата);
Настройки.Вставить("ДатаИзмененияСтрокой", РезультатРазбораДаты.КаноническоеЗначение);
Настройки.Вставить("Лимит", Лимит);
Настройки.Вставить("ЕстьГраница", Ложь);
Возврат Настройки;
КонецФункции
// Формирует запрос с общей сортировкой двух источников сообщений.
Функция ПолучитьТекстЗапросаКомментариев(Настройки)
// Лимит проверен как целое число 1..500; ПЕРВЫЕ принимает числовой литерал.
КоличествоСтрок = Формат(Настройки.Лимит + 1, "ЧГ=0;ЧДЦ=0");
ТекстЗапроса =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Действия.ИспользуемыйДокумент КАК Обращение,
| Действия.ВходящееПисьмо КАК ВходящееПисьмо
|ПОМЕСТИТЬ ВТ_ВходящиеПисьма
|ИЗ
| РегистрСведений.ДействияВходящихПисемПоОбращениям КАК Действия
|ГДЕ
| Действия.Регистрация
| И Действия.ИспользуемыйДокумент <> ЗНАЧЕНИЕ(Документ.Инцидент.ПустаяСсылка)
| И Действия.ВходящееПисьмо <> ЗНАЧЕНИЕ(Документ.ЭлектронноеПисьмоВходящее.ПустаяСсылка)
| И Действия.ВходящееПисьмо.Дата >= &ДатаИзменения
|СГРУППИРОВАТЬ ПО
| Действия.ИспользуемыйДокумент,
| Действия.ВходящееПисьмо
|ИНДЕКСИРОВАТЬ ПО
| Обращение,
| ВходящееПисьмо
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ " + КоличествоСтрок + "
| Сообщения.Дата КАК Дата,
| Сообщения.Источник КАК Источник,
| Сообщения.Обращение КАК Обращение,
| Сообщения.ВидПодчиненного КАК ВидПодчиненного,
| Сообщения.ПодчиненныйИнцидент КАК ПодчиненныйИнцидент,
| Сообщения.ПодчиненныйНаряд КАК ПодчиненныйНаряд,
| Сообщения.UUIDКомментария КАК UUIDКомментария,
| Сообщения.ВходящееПисьмо КАК ВходящееПисьмо,
| Сообщения.НомерИнцидента КАК НомерИнцидента,
| Сообщения.Текст КАК Текст,
| Сообщения.ФорматированныйКомментарий КАК ФорматированныйКомментарий,
| Сообщения.ХранилищеВложений КАК ХранилищеВложений,
| Сообщения.Автор КАК Автор
|ИЗ
| (ВЫБРАТЬ
| Общение.Дата КАК Дата,
| ВЫРАЗИТЬ(0 КАК ЧИСЛО(1, 0)) КАК Источник,
| Общение.Обращение КАК Обращение,
| ВЫРАЗИТЬ(ВЫБОР
| КОГДА Общение.ПодчиненныйДокумент ССЫЛКА Документ.Инцидент
| ТОГДА 1
| КОГДА Общение.ПодчиненныйДокумент ССЫЛКА Документ.Наряд
| ТОГДА 2
| ИНАЧЕ 0
| КОНЕЦ КАК ЧИСЛО(1, 0)) КАК ВидПодчиненного,
| ЕСТЬNULL(
| ВЫРАЗИТЬ(Общение.ПодчиненныйДокумент КАК Документ.Инцидент),
| ЗНАЧЕНИЕ(Документ.Инцидент.ПустаяСсылка)) КАК ПодчиненныйИнцидент,
| ЕСТЬNULL(
| ВЫРАЗИТЬ(Общение.ПодчиненныйДокумент КАК Документ.Наряд),
| ЗНАЧЕНИЕ(Документ.Наряд.ПустаяСсылка)) КАК ПодчиненныйНаряд,
| ВЫРАЗИТЬ(Общение.UUID КАК СТРОКА(36)) КАК UUIDКомментария,
| ЗНАЧЕНИЕ(Документ.ЭлектронноеПисьмоВходящее.ПустаяСсылка) КАК ВходящееПисьмо,
| Общение.Обращение.Номер КАК НомерИнцидента,
| Общение.Комментарий КАК Текст,
| Общение.ФорматированныйКомментарий КАК ФорматированныйКомментарий,
| Общение.ВходящееПисьмо.СтруктураКартинок КАК ХранилищеВложений,
| ПРЕДСТАВЛЕНИЕССЫЛКИ(Общение.Установил) КАК Автор
| ИЗ
| РегистрСведений.ИТ_ОбщениеПоДокументам КАК Общение
| ГДЕ
| Общение.Дата >= &ДатаИзменения
| И Общение.Обращение <> ЗНАЧЕНИЕ(Документ.Инцидент.ПустаяСсылка)
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| ВходящиеПисьма.ВходящееПисьмо.Дата,
| ВЫРАЗИТЬ(1 КАК ЧИСЛО(1, 0)),
| ВходящиеПисьма.Обращение,
| ВЫРАЗИТЬ(0 КАК ЧИСЛО(1, 0)),
| ЗНАЧЕНИЕ(Документ.Инцидент.ПустаяСсылка),
| ЗНАЧЕНИЕ(Документ.Наряд.ПустаяСсылка),
| ВЫРАЗИТЬ("""" КАК СТРОКА(36)),
| ВходящиеПисьма.ВходящееПисьмо,
| ВходящиеПисьма.Обращение.Номер,
| ВходящиеПисьма.ВходящееПисьмо.Текст,
| NULL,
| ВходящиеПисьма.ВходящееПисьмо.СтруктураКартинок,
| ПРЕДСТАВЛЕНИЕССЫЛКИ(ВходящиеПисьма.ВходящееПисьмо.ОтправительКонтакт)
| ИЗ
| ВТ_ВходящиеПисьма КАК ВходящиеПисьма) КАК Сообщения
|%ГРАНИЦА%
|УПОРЯДОЧИТЬ ПО
| Сообщения.Дата,
| Сообщения.Источник,
| Сообщения.Обращение,
| Сообщения.ВидПодчиненного,
| Сообщения.ПодчиненныйИнцидент,
| Сообщения.ПодчиненныйНаряд,
| Сообщения.UUIDКомментария,
| Сообщения.ВходящееПисьмо";
Если Настройки.ЕстьГраница Тогда
Граница =
"ГДЕ
| (Сообщения.Дата > &ПоследняяДата
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник > &ПоследнийИсточник
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение > &ПоследнееОбращение
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение = &ПоследнееОбращение
| И Сообщения.ВидПодчиненного > &ПоследнийВидПодчиненного
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение = &ПоследнееОбращение
| И Сообщения.ВидПодчиненного = &ПоследнийВидПодчиненного
| И Сообщения.ПодчиненныйИнцидент > &ПоследнийПодчиненныйИнцидент
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение = &ПоследнееОбращение
| И Сообщения.ВидПодчиненного = &ПоследнийВидПодчиненного
| И Сообщения.ПодчиненныйИнцидент = &ПоследнийПодчиненныйИнцидент
| И Сообщения.ПодчиненныйНаряд > &ПоследнийПодчиненныйНаряд
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение = &ПоследнееОбращение
| И Сообщения.ВидПодчиненного = &ПоследнийВидПодчиненного
| И Сообщения.ПодчиненныйИнцидент = &ПоследнийПодчиненныйИнцидент
| И Сообщения.ПодчиненныйНаряд = &ПоследнийПодчиненныйНаряд
| И Сообщения.UUIDКомментария > &ПоследнийUUID
| ИЛИ Сообщения.Дата = &ПоследняяДата
| И Сообщения.Источник = &ПоследнийИсточник
| И Сообщения.Обращение = &ПоследнееОбращение
| И Сообщения.ВидПодчиненного = &ПоследнийВидПодчиненного
| И Сообщения.ПодчиненныйИнцидент = &ПоследнийПодчиненныйИнцидент
| И Сообщения.ПодчиненныйНаряд = &ПоследнийПодчиненныйНаряд
| И Сообщения.UUIDКомментария = &ПоследнийUUID
| И Сообщения.ВходящееПисьмо > &ПоследнееВходящееПисьмо)";
Иначе
Граница = "";
КонецЕсли;
Возврат СтрЗаменить(ТекстЗапроса, "%ГРАНИЦА%", Граница);
КонецФункции
// Преобразует строку результата запроса в контракт сообщения.
Функция СформироватьСообщениеКомментариев(Выборка)
Вложения = ПолучитьВложенияСообщенияКомментариев(
Выборка.ФорматированныйКомментарий,
Выборка.ХранилищеВложений);
Сообщение = Новый Структура;
Сообщение.Вставить("incidentId", ПолучитьСтроковоеЗначениеКомментариев(Выборка.НомерИнцидента));
Сообщение.Вставить("incidentUID", Строка(Выборка.Обращение.УникальныйИдентификатор()));
Сообщение.Вставить("id", СформироватьИдентификаторСообщенияКомментариев(Выборка));
Сообщение.Вставить("author", ПолучитьСтроковоеЗначениеКомментариев(Выборка.Автор));
Сообщение.Вставить("text", ПолучитьСтроковоеЗначениеКомментариев(Выборка.Текст));
Сообщение.Вставить("date", XMLСтрока(Выборка.Дата));
Сообщение.Вставить("attachments", Вложения);
Возврат Сообщение;
КонецФункции
// Возвращает изображения форматированного комментария или почтового хранилища.
Функция ПолучитьВложенияСообщенияКомментариев(ФорматированныйКомментарий, ХранилищеВложений)
ВложенияРезультата = Новый Массив;
ДобавитьКартинкиФорматированногоКомментария(ФорматированныйКомментарий, ВложенияРезультата);
Если ВложенияРезультата.Количество() > 0 Тогда
Возврат ВложенияРезультата;
КонецЕсли;
Если ТипЗнч(ХранилищеВложений) = Тип("ХранилищеЗначения") Тогда
ВложенияПисьма = ХранилищеВложений.Получить();
ИначеЕсли ТипЗнч(ХранилищеВложений) = Тип("Массив") Тогда
ВложенияПисьма = ХранилищеВложений;
Иначе
ВложенияПисьма = Неопределено;
КонецЕсли;
Если ВложенияПисьма = Неопределено Тогда
Возврат ВложенияРезультата;
КонецЕсли;
Для Каждого ВложениеКартинка Из ВложенияПисьма Цикл
КартинкаСтрока = ПодготовитьТекстПисьмаИСтруктуруКартинок(ВложениеКартинка.Значение);
Если КартинкаСтрока <> "" Тогда
ВложенияРезультата.Добавить(КартинкаСтрока);
КонецЕсли;
КонецЦикла;
Возврат ВложенияРезультата;
КонецФункции
// Сохраняет полную последнюю сортировочную позицию страницы.
Функция ПолучитьКлючВыборкиКомментариев(Выборка)
Ключ = Новый Структура;
Ключ.Вставить("Дата", Выборка.Дата);
Ключ.Вставить("Источник", Выборка.Источник);
Ключ.Вставить("Обращение", Выборка.Обращение);
Ключ.Вставить("ВидПодчиненного", Выборка.ВидПодчиненного);
Ключ.Вставить("ПодчиненныйИнцидент", Выборка.ПодчиненныйИнцидент);
Ключ.Вставить("ПодчиненныйНаряд", Выборка.ПодчиненныйНаряд);
Ключ.Вставить("UUIDКомментария", Выборка.UUIDКомментария);
Ключ.Вставить("ВходящееПисьмо", Выборка.ВходящееПисьмо);
Возврат Ключ;
КонецФункции
// Формирует стабильный непрозрачный идентификатор из полного ключа источника.
Функция СформироватьИдентификаторСообщенияКомментариев(Выборка)
Ключ = ПолучитьКлючВыборкиКомментариев(Выборка);
ДанныеИдентификатора = Новый Структура;
ДанныеИдентификатора.Вставить("v", 1);
ДанныеИдентификатора.Вставить("source", Ключ.Источник);
ДанныеИдентификатора.Вставить("incidentUID", Строка(Ключ.Обращение.УникальныйИдентификатор()));
ДанныеИдентификатора.Вставить("subordinateKind", Ключ.ВидПодчиненного);
ДанныеИдентификатора.Вставить("subordinateUID", ПолучитьUIDПодчиненногоКомментариев(Ключ));
ДанныеИдентификатора.Вставить("commentUUID", Ключ.UUIDКомментария);
ДанныеИдентификатора.Вставить("emailUID", ПолучитьUIDВходящегоПисьмаКомментариев(Ключ));
Возврат "m1_" + КодироватьJSONBase64URLКомментариев(ДанныеИдентификатора);
КонецФункции
Функция СформироватьКурсорКомментариев(Настройки, ПоследнийКлюч)
ДанныеКурсора = Новый Структура;
ДанныеКурсора.Вставить("v", 1);
ДанныеКурсора.Вставить("endpoint", "getListComments");
ДанныеКурсора.Вставить("changedSince", Настройки.ДатаИзмененияСтрокой);
ДанныеКурсора.Вставить("limit", Настройки.Лимит);
ДанныеКурсора.Вставить("lastDate", XMLСтрока(ПоследнийКлюч.Дата));
ДанныеКурсора.Вставить("lastSource", ПоследнийКлюч.Источник);
ДанныеКурсора.Вставить("lastIncidentUID", Строка(ПоследнийКлюч.Обращение.УникальныйИдентификатор()));
ДанныеКурсора.Вставить("lastSubordinateKind", ПоследнийКлюч.ВидПодчиненного);
ДанныеКурсора.Вставить("lastSubordinateUID", ПолучитьUIDПодчиненногоКомментариев(ПоследнийКлюч));
ДанныеКурсора.Вставить("lastCommentUUID", ПоследнийКлюч.UUIDКомментария);
ДанныеКурсора.Вставить("lastEmailUID", ПолучитьUIDВходящегоПисьмаКомментариев(ПоследнийКлюч));
Возврат КодироватьJSONBase64URLКомментариев(ДанныеКурсора);
КонецФункции
// Проверяет самодостаточный курсор и восстанавливает типизированные границы запроса.
Функция РазобратьКурсорКомментариев(СтрокаКурсора, ТекстОшибки)
Перем Версия, ИмяМетода, ИзмененоПослеСтрокой, Лимит, ПоследняяДатаСтрокой;
Перем ПоследнийИсточник, ПоследнийUIDОбращения, ПоследнийВидПодчиненного;
Перем ПоследнийUIDПодчиненного, ПоследнийUUID, ПоследнийUIDПисьма, ТекстОшибкиДаты;
ТекстОшибки = "Некорректный параметр cursor";
Если ТипЗнч(СтрокаКурсора) <> Тип("Строка")
Или СтрокаКурсора = ""
Или СтрДлина(СтрокаКурсора) > 8192 Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
JSONКурсора = ДекодироватьJSONBase64URLКомментариев(СтрокаКурсора);
Если JSONКурсора = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ЧтениеКурсора = Новый ЧтениеJSON;
ЧтениеКурсора.УстановитьСтроку(JSONКурсора);
ДанныеКурсора = ПрочитатьJSON(ЧтениеКурсора, Ложь);
Исключение
Возврат Неопределено;
КонецПопытки;
Если ТипЗнч(ДанныеКурсора) <> Тип("Структура") Или ДанныеКурсора.Количество() <> 11 Тогда
Возврат Неопределено;
КонецЕсли;
Если Не ДанныеКурсора.Свойство("v", Версия)
Или Не ДанныеКурсора.Свойство("endpoint", ИмяМетода)
Или Не ДанныеКурсора.Свойство("changedSince", ИзмененоПослеСтрокой)
Или Не ДанныеКурсора.Свойство("limit", Лимит)
Или Не ДанныеКурсора.Свойство("lastDate", ПоследняяДатаСтрокой)
Или Не ДанныеКурсора.Свойство("lastSource", ПоследнийИсточник)
Или Не ДанныеКурсора.Свойство("lastIncidentUID", ПоследнийUIDОбращения)
Или Не ДанныеКурсора.Свойство("lastSubordinateKind", ПоследнийВидПодчиненного)
Или Не ДанныеКурсора.Свойство("lastSubordinateUID", ПоследнийUIDПодчиненного)
Или Не ДанныеКурсора.Свойство("lastCommentUUID", ПоследнийUUID)
Или Не ДанныеКурсора.Свойство("lastEmailUID", ПоследнийUIDПисьма) Тогда
Возврат Неопределено;
КонецЕсли;
Если ТипЗнч(Версия) <> Тип("Число") Или Версия <> 1
Или ТипЗнч(ИмяМетода) <> Тип("Строка") Или ИмяМетода <> "getListComments"
Или ТипЗнч(Лимит) <> Тип("Число") Или Лимит <> Цел(Лимит) Или Лимит < 1 Или Лимит > 500
Или ТипЗнч(ПоследнийИсточник) <> Тип("Число")
Или (ПоследнийИсточник <> 0 И ПоследнийИсточник <> 1)
Или ТипЗнч(ПоследнийВидПодчиненного) <> Тип("Число")
Или ПоследнийВидПодчиненного <> Цел(ПоследнийВидПодчиненного)
Или ПоследнийВидПодчиненного < 0 Или ПоследнийВидПодчиненного > 2
Или ТипЗнч(ПоследнийUUID) <> Тип("Строка") Тогда
Возврат Неопределено;
КонецЕсли;
ТекстОшибкиДаты = "";
РезультатРазбораДаты = РазобратьДатуISOКомментариев(ИзмененоПослеСтрокой, ТекстОшибкиДаты);
Если РезультатРазбораДаты = Неопределено
Или РезультатРазбораДаты.КаноническоеЗначение <> ИзмененоПослеСтрокой Тогда
Возврат Неопределено;
КонецЕсли;
ПоследняяДата = ПрочитатьКаноническуюДатуКомментариев(ПоследняяДатаСтрокой);
Если ПоследняяДата = Неопределено Или ПоследняяДата < РезультатРазбораДаты.Дата Тогда
Возврат Неопределено;
КонецЕсли;
ИдентификаторОбращения = ПрочитатьИдентификаторКомментариев(ПоследнийUIDОбращения, Ложь);
Если ИдентификаторОбращения = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ПустойИнцидент = Документы.Инцидент.ПустаяСсылка();
ПустойНаряд = Документы.Наряд.ПустаяСсылка();
ПустоеПисьмо = Документы.ЭлектронноеПисьмоВходящее.ПустаяСсылка();
ПодчиненныйИнцидент = ПустойИнцидент;
ПодчиненныйНаряд = ПустойНаряд;
Если ПоследнийИсточник = 0 Тогда
Если ТипЗнч(ПоследнийUIDПисьма) <> Тип("Строка") Или ПоследнийUIDПисьма <> "" Тогда
Возврат Неопределено;
КонецЕсли;
Если ПоследнийВидПодчиненного = 0 Тогда
Если ТипЗнч(ПоследнийUIDПодчиненного) <> Тип("Строка")
Или ПоследнийUIDПодчиненного <> "" Тогда
Возврат Неопределено;
КонецЕсли;
Иначе
ИдентификаторПодчиненного = ПрочитатьИдентификаторКомментариев(
ПоследнийUIDПодчиненного,
Истина);
Если ИдентификаторПодчиненного = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Если ПоследнийВидПодчиненного = 1 Тогда
ПодчиненныйИнцидент = Документы.Инцидент.ПолучитьСсылку(ИдентификаторПодчиненного);
Иначе
ПодчиненныйНаряд = Документы.Наряд.ПолучитьСсылку(ИдентификаторПодчиненного);
КонецЕсли;
КонецЕсли;
ВходящееПисьмо = ПустоеПисьмо;
Иначе
Если ПоследнийВидПодчиненного <> 0
Или ТипЗнч(ПоследнийUIDПодчиненного) <> Тип("Строка")
Или ПоследнийUIDПодчиненного <> ""
Или ПоследнийUUID <> "" Тогда
Возврат Неопределено;
КонецЕсли;
ИдентификаторПисьма = ПрочитатьИдентификаторКомментариев(ПоследнийUIDПисьма, Ложь);
Если ИдентификаторПисьма = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ВходящееПисьмо = Документы.ЭлектронноеПисьмоВходящее.ПолучитьСсылку(ИдентификаторПисьма);
КонецЕсли;
Настройки = Новый Структура;
Настройки.Вставить("ДатаИзменения", РезультатРазбораДаты.Дата);
Настройки.Вставить("ДатаИзмененияСтрокой", ИзмененоПослеСтрокой);
Настройки.Вставить("Лимит", Лимит);
Настройки.Вставить("ЕстьГраница", Истина);
Настройки.Вставить("ПоследняяДата", ПоследняяДата);
Настройки.Вставить("ПоследнийИсточник", ПоследнийИсточник);
Настройки.Вставить("ПоследнееОбращение", Документы.Инцидент.ПолучитьСсылку(ИдентификаторОбращения));
Настройки.Вставить("ПоследнийВидПодчиненного", ПоследнийВидПодчиненного);
Настройки.Вставить("ПоследнийПодчиненныйИнцидент", ПодчиненныйИнцидент);
Настройки.Вставить("ПоследнийПодчиненныйНаряд", ПодчиненныйНаряд);
Настройки.Вставить("ПоследнийUUID", ПоследнийUUID);
Настройки.Вставить("ПоследнееВходящееПисьмо", ВходящееПисьмо);
Возврат Настройки;
КонецФункции
// Разбирает ISO-дату с обязательным часовым поясом и приводит ее к Москве.
// Ненулевая дробная часть округляется вверх до доступной в 1С целой секунды.
Функция РазобратьДатуISOКомментариев(СтрокаДаты, ТекстОшибки)
ТекстОшибки = "Параметр changedSince должен содержать дату и время ISO с часовым поясом";
Если ТипЗнч(СтрокаДаты) <> Тип("Строка") Или СтрДлина(СтрокаДаты) < 20 Тогда
Возврат Неопределено;
КонецЕсли;
ОсноваДаты = Лев(СтрокаДаты, 19);
Если Сред(ОсноваДаты, 5, 1) <> "-"
Или Сред(ОсноваДаты, 8, 1) <> "-"
Или Сред(ОсноваДаты, 11, 1) <> "T"
Или Сред(ОсноваДаты, 14, 1) <> ":"
Или Сред(ОсноваДаты, 17, 1) <> ":"
Или Не ВсеСимволыЦифрыКомментариев(Лев(ОсноваДаты, 4))
Или Не ВсеСимволыЦифрыКомментариев(Сред(ОсноваДаты, 6, 2))
Или Не ВсеСимволыЦифрыКомментариев(Сред(ОсноваДаты, 9, 2))
Или Не ВсеСимволыЦифрыКомментариев(Сред(ОсноваДаты, 12, 2))
Или Не ВсеСимволыЦифрыКомментариев(Сред(ОсноваДаты, 15, 2))
Или Не ВсеСимволыЦифрыКомментариев(Сред(ОсноваДаты, 18, 2)) Тогда
Возврат Неопределено;
КонецЕсли;
ПозицияПояса = 20;
ЕстьНенулеваяДоляСекунды = Ложь;
Если Сред(СтрокаДаты, ПозицияПояса, 1) = "." Тогда
ПозицияПояса = ПозицияПояса + 1;
НачалоДолиСекунды = ПозицияПояса;
Пока ПозицияПояса <= СтрДлина(СтрокаДаты)
И ВсеСимволыЦифрыКомментариев(Сред(СтрокаДаты, ПозицияПояса, 1)) Цикл
Если Сред(СтрокаДаты, ПозицияПояса, 1) <> "0" Тогда
ЕстьНенулеваяДоляСекунды = Истина;
КонецЕсли;
ПозицияПояса = ПозицияПояса + 1;
КонецЦикла;
Если ПозицияПояса = НачалоДолиСекунды Тогда
Возврат Неопределено;
КонецЕсли;
КонецЕсли;
ЧасовойПояс = Сред(СтрокаДаты, ПозицияПояса);
Если ЧасовойПояс = "Z" Тогда
ИдентификаторЧасовогоПояса = "GMT+0";
ИначеЕсли СтрДлина(ЧасовойПояс) = 6
И (Лев(ЧасовойПояс, 1) = "+" Или Лев(ЧасовойПояс, 1) = "-")
И Сред(ЧасовойПояс, 4, 1) = ":"
И ВсеСимволыЦифрыКомментариев(Сред(ЧасовойПояс, 2, 2))
И ВсеСимволыЦифрыКомментариев(Сред(ЧасовойПояс, 5, 2)) Тогда
ЧасыСмещения = Число(Сред(ЧасовойПояс, 2, 2));
МинутыСмещения = Число(Сред(ЧасовойПояс, 5, 2));
Если ЧасыСмещения > 14 Или МинутыСмещения > 59
Или (ЧасыСмещения = 14 И МинутыСмещения <> 0) Тогда
Возврат Неопределено;
КонецЕсли;
ИдентификаторЧасовогоПояса = "GMT" + ЧасовойПояс;
Иначе
Возврат Неопределено;
КонецЕсли;
Попытка
ДатаБезЧасовогоПояса = XMLЗначение(Тип("Дата"), ОсноваДаты);
Если XMLСтрока(ДатаБезЧасовогоПояса) <> ОсноваДаты Тогда
Возврат Неопределено;
КонецЕсли;
ДатаUTC = УниверсальноеВремя(ДатаБезЧасовогоПояса, ИдентификаторЧасовогоПояса);
ДатаМосква = МестноеВремя(ДатаUTC, "GMT+3");
Если ЕстьНенулеваяДоляСекунды Тогда
ДатаМосква = ДатаМосква + 1;
КонецЕсли;
Исключение
Возврат Неопределено;
КонецПопытки;
Результат = Новый Структура;
Результат.Вставить("Дата", ДатаМосква);
Результат.Вставить("КаноническоеЗначение", XMLСтрока(ДатаМосква) + "+03:00");
Возврат Результат;
КонецФункции
Функция ПрочитатьКаноническуюДатуКомментариев(СтрокаДаты)
Если ТипЗнч(СтрокаДаты) <> Тип("Строка") Или СтрДлина(СтрокаДаты) <> 19 Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
Дата = XMLЗначение(Тип("Дата"), СтрокаДаты);
Если XMLСтрока(Дата) <> СтрокаДаты Тогда
Возврат Неопределено;
КонецЕсли;
Исключение
Возврат Неопределено;
КонецПопытки;
Возврат Дата;
КонецФункции
Функция ПрочитатьИдентификаторКомментариев(СтрокаИдентификатора, РазрешитьПустой)
Если ТипЗнч(СтрокаИдентификатора) <> Тип("Строка") Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
Идентификатор = Новый УникальныйИдентификатор(СтрокаИдентификатора);
Исключение
Возврат Неопределено;
КонецПопытки;
Если НРег(Строка(Идентификатор)) <> НРег(СтрокаИдентификатора) Тогда
Возврат Неопределено;
КонецЕсли;
Если Не РазрешитьПустой И Строка(Идентификатор) = "00000000-0000-0000-0000-000000000000" Тогда
Возврат Неопределено;
КонецЕсли;
Возврат Идентификатор;
КонецФункции
Функция ПолучитьUIDПодчиненногоКомментариев(Ключ)
Если Ключ.ВидПодчиненного = 1 Тогда
Если Ключ.ПодчиненныйИнцидент = Документы.Инцидент.ПустаяСсылка() Тогда
Возврат "00000000-0000-0000-0000-000000000000";
КонецЕсли;
Возврат Строка(Ключ.ПодчиненныйИнцидент.УникальныйИдентификатор());
ИначеЕсли Ключ.ВидПодчиненного = 2 Тогда
Если Ключ.ПодчиненныйНаряд = Документы.Наряд.ПустаяСсылка() Тогда
Возврат "00000000-0000-0000-0000-000000000000";
КонецЕсли;
Возврат Строка(Ключ.ПодчиненныйНаряд.УникальныйИдентификатор());
КонецЕсли;
Возврат "";
КонецФункции
Функция ПолучитьUIDВходящегоПисьмаКомментариев(Ключ)
Если Ключ.Источник = 1 Тогда
Возврат Строка(Ключ.ВходящееПисьмо.УникальныйИдентификатор());
КонецЕсли;
Возврат "";
КонецФункции
Функция ПолучитьЛимитСтраницыКомментариев(СтрокаЛимита, Лимит)
Лимит = 0;
Если ТипЗнч(СтрокаЛимита) <> Тип("Строка")
Или СтрокаЛимита = ""
Или Не ВсеСимволыЦифрыКомментариев(СтрокаЛимита) Тогда
Возврат Ложь;
КонецЕсли;
Попытка
Лимит = Число(СтрокаЛимита);
Исключение
Возврат Ложь;
КонецПопытки;
Возврат Лимит >= 1 И Лимит <= 500 И Лимит = Цел(Лимит);
КонецФункции
Функция ЕстьПараметрHTTPЗапросаКомментариев(ПараметрыЗапроса, ИмяПараметра)
Для Каждого ПараметрЗапроса Из ПараметрыЗапроса Цикл
Если ПараметрЗапроса.Ключ = ИмяПараметра Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Функция ВсеСимволыЦифрыКомментариев(Значение)
Если Значение = "" Тогда
Возврат Ложь;
КонецЕсли;
Для ИндексСимвола = 1 По СтрДлина(Значение) Цикл
Если СтрНайти("0123456789", Сред(Значение, ИндексСимвола, 1)) = 0 Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Функция ПолучитьСтроковоеЗначениеКомментариев(Значение)
Если Значение = Неопределено Или Значение = Null Тогда
Возврат "";
КонецЕсли;
Возврат Строка(Значение);
КонецФункции
Функция КодироватьJSONBase64URLКомментариев(Значение)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Значение);
СтрокаJSON = ЗаписьJSON.Закрыть();
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(СтрокаJSON, КодировкаТекста.UTF8);
СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ДвоичныеДанные);
СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ВК, "");
СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ПС, "");
СтрокаBase64 = СтрЗаменить(СтрокаBase64, "+", "-");
СтрокаBase64 = СтрЗаменить(СтрокаBase64, "/", "_");
Пока Прав(СтрокаBase64, 1) = "=" Цикл
СтрокаBase64 = Лев(СтрокаBase64, СтрДлина(СтрокаBase64) - 1);
КонецЦикла;
Возврат СтрокаBase64;
КонецФункции
Функция ДекодироватьJSONBase64URLКомментариев(СтрокаBase64URL)
ДопустимыеСимволы = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
Для ИндексСимвола = 1 По СтрДлина(СтрокаBase64URL) Цикл
Если СтрНайти(ДопустимыеСимволы, Сред(СтрокаBase64URL, ИндексСимвола, 1)) = 0 Тогда
Возврат Неопределено;
КонецЕсли;
КонецЦикла;
СтрокаBase64 = СтрЗаменить(СтрокаBase64URL, "-", "+");
СтрокаBase64 = СтрЗаменить(СтрокаBase64, "_", "/");
ОстатокДлины = СтрДлина(СтрокаBase64) - Цел(СтрДлина(СтрокаBase64) / 4) * 4;
Если ОстатокДлины = 1 Тогда
Возврат Неопределено;
ИначеЕсли ОстатокДлины = 2 Тогда
СтрокаBase64 = СтрокаBase64 + "==";
ИначеЕсли ОстатокДлины = 3 Тогда
СтрокаBase64 = СтрокаBase64 + "=";
КонецЕсли;
Попытка
ДвоичныеДанные = Base64Значение(СтрокаBase64);
Возврат ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Функция ПолучитьОтветJSONКомментариев(КодСостояния, ЗначениеОтвета)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ЗначениеОтвета);
СтрокаОтвета = ЗаписьJSON.Закрыть();
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json;charset=utf-8");
Ответ.УстановитьТелоИзСтроки(
СтрокаОтвета,
КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецФункции