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

Программист 1С v8.3 (Обычные формы)
← На главную

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

Способ 1: Динамическое формирование текста запроса через метаданные

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

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


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

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

// Удаляем последний лишний оператор "ОБЪЕДИНИТЬ ВСЕ "
ТекстЗапроса = Лев(ТекстЗапроса, СтрДлина(ТекстЗапроса) - 15);

// Добавляем общую сортировку для всего результата
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Дата";

Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ДатаНачала", ДатаНач);
Запрос.УстановитьПараметр("ДатаКонца", ДатаКонец);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

Способ 2: Использование системных журналов документов

Выясним, можно ли упростить задачу. Если в вашей конфигурации (например, в «Управлении производственным предприятием» или «ERP») определен Общий журнал документов или журнал Полный, в который включены все виды документов, запрос станет на порядок проще и быстрее. Журнал — это фактически одна физическая таблица в базе данных, оптимизированная для подобных выборок.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Ссылка,
    |   Дата
    |ИЗ
    |   ЖурналДокументов.Полный
    |ГДЕ
    |   Дата МЕЖДУ &ДатаНачала И &ДатаКонца
    |УПОРЯДОЧИТЬ ПО
    |   Дата";

Запрос.УстановитьПараметр("ДатаНачала", ДатаНач);
Запрос.УстановитьПараметр("ДатаКонца", ДатаКонец);
Выборка = Запрос.Выполнить().Выбрать();

Использование журнала позволяет избежать динамической склейки текста и снижает нагрузку на систему. Рекомендуем сначала проверить наличие такого журнала в вашей конфигурации.

Программное проведение документов и обработка ошибок

После того как мы получили упорядоченный список ссылок, перейдем к их проведению. Здесь важно не совершить типичную ошибку начинающего разработчика — попытку вызвать метод Записать() непосредственно у ссылки. Ссылка — это лишь уникальный идентификатор в базе. Чтобы изменить данные, необходимо получить объект документа.

Рассмотрим правильный алгоритм проведения. Проанализируем необходимость использования неоперативного режима. Поскольку мы работаем с документами за период (вероятно, «задним числом»), нам обязательно нужно указывать РежимПроведенияДокумента.Неоперативный. Это позволит избежать ошибок, связанных с проверкой остатков на текущую секунду времени, если в системе включен оперативный контроль.


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

    Попытка
        // Выполняем проведение в неоперативном режиме
        ДокОбъект.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
        Сообщить("Документ " + Выборка.Ссылка + " успешно проведен.");
    Исключение
        // Выводим подробное описание ошибки для диагностики
        ТекстОшибки = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
        Сообщить("ОШИБКА: Документ " + Выборка.Ссылка + " не проведен! Причина: " + ТекстОшибки);
    КонецПопытки;
    
КонецЦикла;

Использование функции КраткоеПредставлениеОшибки() крайне важно, так как она покажет реальную причину отказа в проведении (например, «Не заполнено обязательное поле» или «Недостаточно товара на складе»), что позволит оперативно исправить данные.

Риски массового перепроведения в сложных конфигурациях

Посмотрим на ситуацию со стороны стабильности системы. Если вы работаете в конфигурации типа УПП (Управление производственным предприятием), программное проведение всех документов за прошлый период несет в себе серьезные риски:

  1. Смещение границы последовательностей: В УПП партионный и налоговый учет жестко привязаны к моменту времени. Проведение хотя бы одного старого документа «сбивает» границу, и вам придется перепроводить абсолютно всё до текущего момента, чтобы восстановить корректную себестоимость.
  2. Изменение отчетности: Если период уже закрыт и отчетность сдана, перепроведение может изменить движения регистров, что приведет к расхождению с данными налоговой декларации или баланса.
  3. Транзакционные блокировки: Массовая запись объектов создает нагрузку на SQL-сервер. Рекомендуется выполнять такие операции в моменты минимальной активности пользователей.

Перед запуском подобных скриптов в рабочей базе обязательно создавайте резервную копию или сначала тестируйте код на копии базы данных.

Альтернативные стандартные способы

Выясним, всегда ли нужно писать код. В типовых решениях часто встроены механизмы, которые справятся с этой задачей безопаснее:

Если же ваша задача уникальна и требует именно программного подхода, используйте вышеописанные методы с учетом оптимизации запросов и обработки исключений.

← На главную