При разработке HTTP-сервисов в 1С часто возникает задача получения данных за определенный период. Казалось бы, задача тривиальная: получить параметры (год, месяц, день), найти документы и вернуть их список. Однако на этом пути нас подстерегают три основные проблемы: типизация входящих данных, производительность запроса к базе данных и корректная сериализация ответа. Давайте подробно разберем каждую из них и составим правильный алгоритм решения.
В рассматриваемом случае параметры передаются в URL отдельными частями: год, месяц и день. Первая ошибка, с которой мы сталкиваемся — это несоответствие типов.
Метод Запрос.ПараметрыURL.Получить("ИмяПараметра") всегда возвращает значение типа Строка (или Неопределено, если параметр не передан). Конструктор даты или параметры запроса часто ожидают Число или Дату. Если передать строку напрямую в функцию создания даты, возникнет ошибка.
Рассмотрим, как правильно получить и преобразовать данные:
// Получаем параметры как строки
ГодСтрока = Запрос.ПараметрыURL.Получить("God");
МесяцСтрока = Запрос.ПараметрыURL.Получить("Mesac");
ДеньСтрока = Запрос.ПараметрыURL.Получить("Den");
// Проверяем заполненность и преобразуем в Число
Попытка
ГодЧисло = Число(ГодСтрока);
МесяцЧисло = Число(МесяцСтрока);
ДеньЧисло = Число(ДеньСтрока);
// Формируем дату 1С
ИскомаяДата = Дата(ГодЧисло, МесяцЧисло, ДеньЧисло);
Исключение
// Обработка ошибки, если переданы некорректные данные
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Неверный формат даты");
Возврат Ответ;
КонецПопытки;
Таким образом, мы гарантируем, что у нас есть переменная ИскомаяДата правильного типа, с которой можно работать дальше.
Это, пожалуй, самый важный момент. В исходном коде использовалась следующая конструкция:
ГДЕ
ГОД(ПоступлениеТоваров.Дата) = &God
И МЕСЯЦ(ПоступлениеТоваров.Дата) = &Mesac
И ДЕНЬ(ПоступлениеТоваров.Дата) = &Den
Почему так делать нельзя? Использование функций (ГОД, МЕСЯЦ, ДЕНЬ, НАЧАЛОПЕРИОДА) над полем таблицы в секции ГДЕ отключает использование индексов SQL. СУБД вынуждена сканировать всю таблицу (Full Scan), вычислять значения для каждой строки и сравнивать их. На больших объемах данных это приведет к критическому падению производительности.
Правильное решение: использовать диапазон дат. Нам нужно отобрать документы за конкретные сутки. Для этого мы определим начало и конец этого дня и используем оператор МЕЖДУ. В этом случае SQL-сервер сможет использовать индекс по полю Дата.
Подготовим параметры для запроса:
// Определяем границы дня
НачалоДня = НачалоДня(ИскомаяДата);
КонецДня = КонецДня(ИскомаяДата);
И изменим текст запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеТоваров.Номер КАК Номер,
| ПоступлениеТоваров.Дата КАК Дата,
| ПоступлениеТоваров.Контрагент.Представление КАК Контрагент,
| ПоступлениеТоваров.Склад.Представление КАК Склад,
| ПоступлениеТоваров.СуммаДокумента КАК Сумма
|ИЗ
| Документ.ПоступлениеТоваров КАК ПоступлениеТоваров
|ГДЕ
| ПоступлениеТоваров.Дата МЕЖДУ &НачалоДня И &КонецДня";
Запрос.УстановитьПараметр("НачалоДня", НачалоДня);
Запрос.УстановитьПараметр("КонецДня", КонецДня);
Третья распространенная проблема — ошибки при сериализации в JSON, которые часто возникают при работе с внешними API, например, с авторизацией через RSA. Разберем основные нюансы:
УстановитьСтроку() у объекта ЗаписьJSON.text/plainapplication/json является ошибочным (смешаны два типа). Правильный вариант: application/json.НастройкиСериализацииJSON ручное преобразование дат в строку часто не требуется, платформа сделает это сама в формат ISO.Соберем данные в массив структур и сериализуем их (чтобы не дублировать логику, можно использовать готовые процедуры из общего модуля):
Выборка = Запрос.Выполнить().Выбрать();
МассивДанных = Новый Массив;
Пока Выборка.Следующий() Цикл
СтруктураСтроки = Новый Структура;
СтруктураСтроки.Вставить("Number", Выборка.Номер);
СтруктураСтроки.Вставить("Date", Выборка.Дата); // 1С сама преобразует дату в строку JSON
СтруктураСтроки.Вставить("Partner", Выборка.Контрагент);
СтруктураСтроки.Вставить("Warehouse", Выборка.Склад);
СтруктураСтроки.Вставить("Amount", Выборка.Сумма);
МассивДанных.Добавить(СтруктураСтроки);
КонецЦикла;
// Настройка записи JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(); // Важно: пишем в память (строку)
// Параметры форматирования дат
НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата; // Формат ISO 8601
// Записываем массив
ЗаписатьJSON(ЗаписьJSON, МассивДанных, НастройкиСериализации);
// Получаем итоговую строку
СтрокаJSON = ЗаписьJSON.Закрыть();
Теперь объединим все исправленные части в один готовый блок кода, который можно использовать в модуле HTTP-сервиса или в составе универсальных библиотек для разработки API.
Функция ПолучитьДокументы(Запрос)
// 1. Получение и валидация параметров
ГодStr = Запрос.ПараметрыURL.Получить("God");
МесStr = Запрос.ПараметрыURL.Получить("Mesac");
ДеньStr = Запрос.ПараметрыURL.Получить("Den");
Попытка
ДатаЗапроса = Дата(Число(ГодStr), Число(МесStr), Число(ДеньStr));
Исключение
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Ошибка формата даты. Ожидаются числа: God, Mesac, Den");
Возврат Ответ;
КонецПопытки;
// 2. Выполнение запроса (Оптимизировано через МЕЖДУ)
ЗапросSQL = Новый Запрос;
ЗапросSQL.Текст =
"ВЫБРАТЬ
| ПоступлениеТоваров.Номер КАК Номер,
| ПоступлениеТоваров.Дата КАК Дата,
| ПоступлениеТоваров.Контрагент.Представление КАК Контрагент,
| ПоступлениеТоваров.СуммаДокумента КАК Сумма
|ИЗ
| Документ.ПоступлениеТоваров КАК ПоступлениеТоваров
|ГДЕ
| ПоступлениеТоваров.Дата МЕЖДУ &НачалоДня И &КонецДня";
ЗапросSQL.УстановитьПараметр("НачалоДня", НачалоДня(ДатаЗапроса));
ЗапросSQL.УстановитьПараметр("КонецДня", КонецДня(ДатаЗапроса));
Результат = ЗапросSQL.Выполнить();
Выборка = Результат.Выбрать();
МассивРезультат = Новый Массив;
Пока Выборка.Следующий() Цикл
Данные = Новый Структура("Number, Date, Partner, Amount");
ЗаполнитьЗначенияСвойств(Данные, Выборка);
МассивРезультат.Добавить(Данные);
КонецЦикла;
// 3. Сериализация в JSON
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
Настройки = Новый НастройкиСериализацииJSON;
Настройки.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата;
ЗаписатьJSON(Запись, МассивРезультат, Настройки);
ТекстJSON = Запись.Закрыть();
// 4. Формирование ответа
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(ТекстJSON);
Возврат Ответ;
КонецФункции
Используя этот подход, мы обеспечиваем высокую скорость работы запроса благодаря индексам, корректную обработку входящих данных и валидный JSON-ответ для клиента.