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