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