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