При массовой обработке документов, особенно в старых конфигурациях вроде 1С:Управление торговлей 10.3, программисты часто сталкиваются с ситуацией, когда цикл перепроведения прерывается из-за непредвиденных ошибок. Это может быть связано как с логическими ошибками в коде, так и с интерактивными вызовами (модальными окнами), которые останавливают выполнение алгоритма до реакции пользователя. Разберем подробно, как правильно организовать цикл обработки, чтобы он не прерывался, и какие технические нюансы необходимо учесть.
Разберем ситуацию, когда стандартная конструкция Попытка ... Исключение не спасает от остановки цикла. Проанализируем код, который часто становится причиной проблемы:
Пока Выборка.Следующий() Цикл
НачатьТранзакцию();
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
Попытка
ТекОбъект.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Исключение
// Вот здесь кроется главная ошибка
Сообщить("Проблема в проведении: " + Выборка.Ссылка);
ВызватьИсключение;
КонецПопытки;
КонецЦикла;
Выясним причину остановки. Оператор ВызватьИсключение (или Raise) внутри блока Исключение фактически заново выбрасывает пойманную ошибку в вышестоящий уровень исполнения. Если над этим циклом нет другой "попытки", платформа 1С интерпретирует это как критическую ошибку, выводит модальное окно и полностью прекращает выполнение обработки. Чтобы цикл продолжался, необходимо просто фиксировать ошибку (например, в журнале регистрации или через Сообщить) и переходить к следующей итерации, не вызывая исключение повторно.
Одной из скрытых причин остановки циклов является некорректная работа с транзакциями. Рассмотрим механизм: если внутри транзакции (после НачатьТранзакцию) происходит ошибка, транзакция помечается системой как "битая". В таком состоянии любая попытка зафиксировать её или начать новую транзакцию приведет к системной ошибке "В данной транзакции уже происходили ошибки".
Разберем по шагам правильную структуру кода с учетом транзакций:
ОтменитьТранзакцию() в блоке Исключение, если транзакция была начата.ТранзакцияАктивна().Посмотрим на пример правильной реализации:
Пока Выборка.Следующий() Цикл
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
НачатьТранзакцию();
Попытка
ТекОбъект.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Сообщить("Успешно: " + ТекОбъект);
Исключение
// Отменяем транзакцию, чтобы освободить ресурсы и сбросить состояние ошибки
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
Сообщить("Ошибка при проведении " + ТекОбъект + ": " + ОписаниеОшибки());
// Здесь НЕ используем ВызватьИсключение, чтобы цикл шел дальше
КонецПопытки;
КонецЦикла;
Часто в старых конфигурациях в модулях документов прописаны проверки, которые вызывают Предупреждение() или Вопрос(). В программном цикле такие вызовы создают модальные окна, которые "вешают" процесс до тех пор, пока человек не нажмет кнопку. Чтобы обойти прикладные проверки, разработан механизм ОбменДанными.Загрузка.
Проанализируем ситуацию: когда вы устанавливаете свойство ТекОбъект.ОбменДанными.Загрузка = Истина, типовые механизмы 1С (в процедурах ПередЗаписью, ПриЗаписи, ОбработкаПроверкиЗаполнения) понимают, что данные записываются программно (например, при обмене или массовой обработке), и пропускают большинство интерактивных проверок.
Применим это в нашем коде:
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
ТекОбъект.ОбменДанными.Загрузка = Истина;
Попытка
ТекОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Обработка ошибки
КонецПопытки;
Важно: этот флаг может отключать и важные бизнес-проверки (например, контроль остатков), поэтому используйте его осознанно, только если уверены в корректности данных прошлых периодов.
Если конфигурация позволяет (платформа 8.3), наиболее надежным способом избежать "стопора" из-за модальных окон является вынос процедуры проведения в фоновое задание на сервере. На стороне сервера интерактивные методы (окна сообщений, вопросы) физически не могут быть отображены. В этом случае система либо автоматически проигнорирует вызов окна, либо выдаст ошибку, которую блок Попытка успешно перехватит, позволив циклу двигаться дальше.
Рассмотрим преимущества этого подхода:
При обработке десятков тысяч документов использование метода Сообщить() может существенно замедлить работу и даже привести к нехватке памяти у клиентского сеанса. Рекомендуем записывать ошибки в системный Журнал регистрации. Это позволит вам детально проанализировать причины неудач после завершения всего цикла.
Пример использования ЗаписьЖурналаРегистрации:
Исключение
ЗаписьЖурналаРегистрации("МассовоеПроведение",
УровеньЖурналаРегистрации.Ошибка,
ТекОбъект.Метаданные(),
ТекОбъект.Ссылка,
"Ошибка: " + ОписаниеОшибки());
КонецПопытки;
Существуют "неуловимые" ошибки, которые Попытка не сможет обработать. К ним относятся:
потеря соединения с сервером БД, критическая нехватка памяти (Out of Memory) или сбои в структуре таблиц. В этих случаях выполнение прервется вместе с сеансом.
Для минимизации последствий таких сбоев рекомендуем:
Соблюдение этих правил позволит вам создать отказоустойчивый скрипт для перепроведения документов даже в самых сложных и запущенных базах данных.