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