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