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