Как найти документы за определенный период, в которых используется конкретное подразделение

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе сопровождения систем на базе 1С часто возникает задача поиска документов, в которых задействованы определенные объекты, например, аннулированные подразделения, уволенные сотрудники или закрытые склады. Стандартные инструменты, такие как отчет Места использования или стандартная функция Поиск ссылок на объект, не всегда подходят, так как они либо выводят слишком много «мусорной» информации из регистров, либо не позволяют наложить фильтр по дате создания документов. Рассмотрим подробно, как решить эту задачу программными методами, проанализируем архитектурные подходы и разберем примеры кода.

Проблема стандартных механизмов поиска

Разберем, почему типовые средства могут оказаться неэффективными. Во-первых, поиск ссылок через контекстное меню объекта анализирует всю базу данных, включая движения по регистрам накопления и сведений. Если нам нужно найти только документы, мы получаем огромный список записей, который невозможно быстро отфильтровать по периоду. Во-вторых, попытка использовать Консоль запросов для выбора всех типов документов приводит к тому, что конструктор запросов автоматически создает связи между таблицами, которые приходится удалять вручную, что крайне трудоемко при наличии десятков видов документов.

Метод 1: Программный анализ метаданных и сборка динамического запроса

Этот способ является наиболее гибким и эффективным. Мы не будем перебирать все документы в базе, а сначала определим, какие именно типы документов в принципе могут содержать реквизит с типом СправочникСсылка.Подразделения. Проанализируем ситуацию: нам нужно собрать текст запроса только по тем таблицам, которые нам интересны. Рассмотрим по шагам, как это реализовать.

Сначала мы создадим массив типов документов, которые имеют реквизит нужного нам типа. Затем мы динамически сформируем текст запроса, объединяя таблицы через оператор ОБЪЕДИНИТЬ ВСЕ.


// Определим массив искомых подразделений
МассивПодразделений = Новый Массив;
МассивПодразделений.Добавить(СсылкаНаАннулированноеПодразделение);

// Настроим параметры запроса
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачалаПоиска);
Запрос.УстановитьПараметр("СписокПодразделений", МассивПодразделений);

ТекстыЗапросов = Новый Массив;

// Проходим циклом по всем документам в метаданных
Для Каждого МетаДанныеДокумента Из Метаданные.Документы Цикл
    
    НужныйТипНайден = Ложь;
    ИмяРеквизитаПодразделение = "";
    
    // Проверяем реквизиты шапки
    Для Каждого Реквизит Из МетаДанныеДокумента.Реквизиты Цикл
        Если Реквизит.Тип.СодержитТип(Тип("СправочникСсылка.Подразделения")) Тогда
            НужныйТипНайден = Истина;
            ИмяРеквизитаПодразделение = Реквизит.Имя;
            
            // Формируем текст запроса для конкретной таблицы
            Текст = "ВЫБРАТЬ Ссылка, Дата, Подразделение ИЗ Документ." + МетаДанныеДокумента.Имя + 
                    " ГДЕ Дата >= &ДатаНачала И " + ИмяРеквизитаПодразделение + " В (&СписокПодразделений)";
            ТекстыЗапросов.Добавить(Текст);
        КонецЕсли;
    КонецЦикла;
    
    // Дополнительно можно проверить табличные части, если подразделение может быть там
    Для Каждого ТЧ Из МетаДанныеДокумента.ТабличныеЧасти Цикл
        Для Каждого РеквизитТЧ Из ТЧ.Реквизиты Цикл
            Если РеквизитТЧ.Тип.СодержитТип(Тип("СправочникСсылка.Подразделения")) Тогда
                // Здесь логика сборки запроса для ТЧ будет немного сложнее (с использованием СГРУППИРОВАТЬ ПО)
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    
КонецЦикла;

// Объединяем все найденные тексты в один запрос
Если ТекстыЗапросов.Количество() > 0 Тогда
    Запрос.Текст = СтрСоединить(ТекстыЗапросов, " ОБЪЕДИНИТЬ ВСЕ ");
    Результат = Запрос.Выполнить().Выгрузить();
КонецЕсли;

Важный нюанс: при использовании большого количества операторов ОБЪЕДИНИТЬ ВСЕ (если документов в системе сотни), SQL-сервер может столкнуться с ограничением по глубине вложенности запроса. В таких случаях лучше выполнять запросы порциями или использовать временные таблицы.

Метод 2: Использование функции НайтиПоСсылкам с постобработкой

Рассмотрим ситуацию, когда нам не хочется вручную перебирать метаданные. Мы можем воспользоваться платформенным методом НайтиПоСсылкам, но оптимизировать его работу. Выясним, как это сделать быстрее.

Основная проблема НайтиПоСсылкам — сканирование всех таблиц. Однако метод позволяет передать вторым параметром Область поиска. Проанализируем пример кода, который сначала находит все вхождения объекта, а затем фильтрует их по типу (только документы) и дате.


// Подготовим массив объектов для поиска
МассивОбъектов = Новый Массив;
МассивОбъектов.Добавить(АннулированноеПодразделение);

// Выполняем поиск ссылок по всей базе
ТаблицаСсылок = НайтиПоСсылкам(МассивОбъектов);

// Создаем таблицу для итогового результата
ИтоговаяТаблица = Новый ТаблицаЗначений;
ИтоговаяТаблица.Колонки.Добавить("ДокументСсылка");
ИтоговаяТаблица.Колонки.Добавить("Дата");

Для Каждого СтрокаСсылки Из ТаблицаСсылок Цикл
    
    // Нас интересуют только документы
    МетаданныеОбъекта = СтрокаСсылки.Данные.Метаданные();
    Если Метаданные.Документы.Содержит(МетаданныеОбъекта) Тогда
        
        // Проверяем дату документа
        Если СтрокаСсылки.Данные.Дата >= ДатаНачала Тогда
            НоваяСтрока = ИтоговаяТаблица.Добавить();
            НоваяСтрока.ДокументСсылка = СтрокаСсылки.Данные;
            НоваяСтрока.Дата = СтрокаСсылки.Данные.Дата;
        КонецЕсли;
        
    КонецЕсли;
    
КонецЦикла;

Недостаток этого метода в том, что обращение к свойству СтрокаСсылки.Данные.Дата вызывает обращение к базе данных для получения объекта. Если ссылок тысячи, это будет работать медленно. Поэтому оптимальнее использовать подход из Метода 1, где данные выбираются одним запросом.

Метод 3: Использование механизма «Критерии отбора»

Если задача поиска по подразделению является регулярной, рекомендуем использовать объект метаданных Критерий отбора. Посмотрим, как это работает. В конфигураторе создается новый объект КритерииОтбора (например, «ПоПодразделению»), указывается тип значения и перечисляются реквизиты документов, которые входят в этот критерий.

Преимущества этого подхода:

  1. Скорость: Платформа создает специальный индекс в базе данных для этого критерия.
  2. Удобство: Получить список документов можно одной строкой кода: КритерииОтбора.ПоПодразделению.Найти(НужноеПодразделение).
  3. Стандартный интерфейс: Ссылки по критерию отбора можно вывести в форму элемента справочника «Подразделения» через панель навигации.

Метод 4: Использование инструментов Библиотеки стандартных подсистем (БСП)

Если вы работаете в современной конфигурации (ERP, УТ 11, БП 3.0), в ней наверняка интегрирована БСП. В составе этой библиотеки есть подсистема «Запрет редактирования» или «Удаление объектов», которые содержат программные интерфейсы для эффективного поиска ссылок.

Например, обработка ОбщиеФормы.МестаИспользованияОбъекта позволяет визуализировать ссылки. Также можно программно обратиться к методам модуля ОбщегоНазначения. Проанализируем возможность использования функции ОбщегоНазначения.ПараметрыВыбораОбъекта или аналогичных для анализа структуры метаданных.

Оптимизация производительности

При реализации собственного отчета обратите внимание на следующие моменты:

Подводя итог, скажем: для разовой задачи проще всего использовать модифицированный код с НайтиПоСсылкам и фильтрацией в цикле. Для постоянной работы в крупных базах лучше один раз настроить Критерий отбора или написать внешнюю обработку, которая динамически строит запрос на основе анализа Метаданные.Документы, как мы разобрали в первом методе.

← На главную