Разработка мобильных приложений на платформе 1С имеет множество специфических нюансов, о которых разработчики десктопных решений часто даже не подозревают. Одна из таких крайне неприятных проблем — когда динамический список, построенный на основе произвольного запроса, возвращает абсолютно пустой результат на мобильном устройстве, хотя тот же самый код на десктопной платформе работает корректно и выводит нужные данные.
Особенно часто это происходит, когда в запросе динамического списка используется фильтрация по входящему массиву через оператор В (&Параметр). В этой статье мы подробно разберем, почему возникает эта проблема, как на нее влияют особенности СУБД мобильного устройства и какие существуют эффективные способы обхода этой ошибки.
Представим стандартную задачу: нам необходимо ограничить видимость документов в динамическом списке мобильного приложения только теми объектами, которые разрешены текущему пользователю. Для этого мы передаем массив разрешенных ссылок в параметр запроса динамического списка:
ВЫБРАТЬ
ДокументЗамечания.Ссылка КАК Ссылка,
ДокументЗамечания.Дата КАК Дата,
ДокументЗамечания.Номер КАК Номер
ИЗ
Документ.Замечания КАК ДокументЗамечания
ГДЕ
ДокументЗамечания.Ссылка В (&Разрешенные)
На десктопной платформе этот запрос отрабатывает моментально и возвращает список документов. Однако на мобильной платформе (особенно версий 8.3.12–8.3.13) результат оказывается пустым. Давайте проанализируем технические причины такого поведения.
Существует две основные причины возникновения этой ошибки:
Неопределено, значение типа Null или битая ссылка на другой объект, оставшаяся после некорректной синхронизации), СУБД SQLite не может выполнить неявное приведение типов для оператора IN. В результате сравнение завершается ошибкой на уровне базы данных, и SQLite просто возвращает пустой результат для всего запроса, не генерируя при этом видимого исключения в коде 1С.В (&Параметр), конструктор СКД не всегда автоматически выставлял для этого параметра флаг «Доступен список значений». Платформа считала параметр одиночным. При попытке передать туда массив или список значений происходила некорректная подстановка: передавался либо только первый элемент коллекции, либо вся выборка сбрасывалась в пустоту.Ниже мы подробно рассмотрим три практических способа решения этой проблемы, начиная от самого надежного и рекомендуемого.
Самый надежный способ обойти ограничения трансляции запросов в SQLite и баги СКД — полностью отказаться от использования параметра В (&Параметр) непосредственно в тексте запроса динамического списка. Вместо этого мы перенесем наложение фильтра в механизм компоновщика настроек динамического списка.
Рассмотрим по шагам, как это реализовать:
Шаг 1. Модифицируем текст запроса динамического списка, убирая из него секцию ГДЕ с параметром:
ВЫБРАТЬ
ДокументЗамечания.Ссылка КАК Ссылка,
ДокументЗамечания.Дата КАК Дата,
ДокументЗамечания.Номер КАК Номер
ИЗ
Документ.Замечания КАК ДокументЗамечания
Шаг 2. В коде модуля формы при создании или при получении списка разрешенных документов устанавливаем фиксированный отбор программно:
&НаСервере
Процедура НастроитьОтборНаСервере(МассивРазрешенных)
// Определяем поле, по которому будем делать отбор
ПолеОтбора = Новый ПолеКомпоновкиДанных("Ссылка");
// Сначала найдем и удалим старый отбор по этому полю, если он уже существовал
Индекс = 0;
Пока Индекс < Список.Отбор.Элементы.Количество() Цикл
ЭлементОтбора = Список.Отбор.Элементы[Индекс];
Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных")
И ЭлементОтбора.ЛевоеЗначение = ПолеОтбора Тогда
Список.Отбор.Элементы.Удалить(ЭлементОтбора);
Иначе
Индекс = Index + 1;
КонецЕсли;
КонецЦикла;
// Добавляем новый элемент отбора
НовыйОтбор = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйОтбор.ЛевоеЗначение = ПолеОтбора;
НовыйОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
НовыйОтбор.ПравоеЗначение = МассивРазрешенных;
НовыйОтбор.Использование = Истина;
НовыйОтбор.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецПроцедуры
Почему этот способ работает стабильно? Дело в том, что когда мы накладываем отбор через компоновщик настроек Список.Отбор, платформа 1С самостоятельно и максимально корректно транслирует это условие в понятный для SQLite синтаксис, избегая проблем с типизацией параметров.
Если по каким-то причинам вам необходимо оставить условие фильтрации непосредственно внутри текста запроса, вам придется гарантировать идеальную чистоту передаваемого массива. SQLite не прощает присутствия «мусорных» типов.
Нам необходимо исключить из массива любые значения, которые не соответствуют целевому типу данных (в нашем примере это тип ДокументСсылка.Замечания). Особое внимание нужно уделить исключению значений Неопределено и Null, которые часто возвращаются функциями синхронизации.
Посмотрим на пример функции очистки и подготовки массива:
&НаСервере
Процедура УстановитьОчищенныйПараметрЗапроса(ИсходныйМассив)
ОчищенныйМассив = Новый Массив;
Для Каждого Элемент Из ИсходныйМассив Цикл
// Проверяем тип данных каждого элемента массива
Если ТипЗнч(Элемент) = Тип("ДокументСсылка.Замечания") Тогда
// Исключаем пустые ссылки, они также могут мешать сопоставлению типов в SQLite
Если НЕ Элемент.Пустая() Тогда
ОчищенныйМассив.Добавить(Элемент);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Передаем только гарантированно очищенный массив строго одного типа
Список.Параметры.УстановитьЗначениеПараметра("Разрешенные", ОчищенныйМассив);
КонецПроцедуры
Этот способ позволяет решить проблему в тех случаях, когда структура метаданных простая, а массив содержит только валидные и строго типизированные ссылки.
В некоторых случаях разработчики пытаются заменить массив объектом типа СписокЗначений. Однако здесь важно помнить о баге старых версий мобильной платформы, при котором в параметр запроса уходит только первый элемент из списка значений.
Если вы используете этот подход, обязательно убедитесь, что вы не просто передаете объект СписокЗначений, но и принудительно выставляете тип значения для этого параметра в схеме компоновки данных динамического списка. Для этого откройте свойства параметра в конфигураторе и в поле «Доступен список значений» установите флаг в значение Истина. Только в этом случае платформа корректно передаст всю коллекцию.
Проанализировав проблему пустых динамических списков на мобильной платформе 1С, можно сделать однозначный вывод: наиболее стабильным и архитектурно правильным решением является использование фиксированных отборов компоновщика настроек (Решение 1). Этот подход полностью перекладывает задачу генерации SQL-кода на платформу 1С, гарантируя корректную обработку типов данных в СУБД SQLite без необходимости ручной очистки массивов и без риска натолкнуться на системные баги старых версий мобильной платформы.