Суть проблемы: предположим, у некоего произвольного документа поменялся механизм, формирующий проводки по некоторому регистру.
При этом, по тем или иным причинам, мы не хотим использовать групповое перепроведение документов - например, мы не хотим, чтобы перепроведённый документ зарегистрировался для синхронизации.
Задача: переотразить движения заранее известной выборки документов по заранее известному списку регистров, но НЕ перезаписывая документ. В идеале - не тратя время на анализ того, как конкретно формируются движения документа.
Решение будем рассматривать на базе конфигурации Бухгалтерия Предприятия 3, на примере документа РеализацияТоваровИУслуг. В данном примере поменялся алгоритм проведения по регистру Хозрасчетный. Посмотрим на примере простейшего, косметического изменения в обработчике ПриПроведении (скриншот изменения - ниже) - однако алгоритм не трудно адаптировать под другие регистры, другие документы и даже другие конфигурации.
Замечание: в практике автора изменение алгоритма проведения было сильно сложнее, но к сути статьи это не относится.

Ход решения:
1) Сформируем выборку документов, которые нам надо переотразить. Например, мы хотим переотразить все РТиУ с 2025-го года и далее, движения которых не были откорректированы - ни нашей обработкой, ни вручную.
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 10 //Можно убрать ""Первые 10"", но на реальной базе это будет работать несколько часов, а обработка демонстрационная.
| Реализация.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата >= &Дата
| И НЕ (ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(7))) = ""#КорРег""
| И Реализация.Проведен
| И НЕ Реализация.РучнаяКорректировка
|
|УПОРЯДОЧИТЬ ПО
| Реализация.Дата,
| Реализация.Номер");
Дата = '202501010000';
Запрос.УстановитьПараметр("Дата", Дата);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Выгрузить().Количество() = 0 Тогда
ОбщегоНазначения.СообщитьПользователю("Мы всё переотразили! Ура!");
Иначе
ОбщегоНазначения.СообщитьПользователю("Обратите внимание - возможно не всё переотражено");
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Шаг 2: перепроведём каждый отдельный документ, запомнив его движения - и тут же отменим транзакцию, чтобы в результате у нас не было никаких изменений в базе, но был актуальный состав движений по регистрам.
Замечание: поскольку алгоритм универсальный, запоминаются ВСЕ проводки - но если мы заранее знаем, по какому регистру мы хотим выполнить отражение, можно сделать чуть проще, чем в приведённом коде.
КоличествоРегистров = 0;
Пока Выборка.Следующий() Цикл
Попытка
НовыеТаблицыПроводок = Новый Массив;
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
//Это не совсем "групповое перепроведение", но это допсвойство позволит пропустить много "тяжёлых" операций.
ДокОбъект.ДополнительныеСвойства.Вставить("ГрупповоеПерепроведение", Истина);
//Создадим новые проводки документа - проще всего это сделать, проведя документ. Потом откатим транзакцию.
НачатьТранзакцию();
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Если КоличествоРегистров = 0 Тогда
КоличествоРегистров = ДокОбъект.Движения.Количество();
КонецЕсли;
Счетчик = 0;
Пока Счетчик < КоличествоРегистров Цикл
НоваяТаблицаПроводок = Новый Структура("Номер, Таблица");
НоваяТаблицаПроводок.Номер = Счетчик; //Синтакс-помощник не обещает, что при обходе движений документа обход будет упорядочен
НоваяТаблицаПроводок.Таблица = ДокОбъект.Движения[Счетчик].Выгрузить();
Счетчик = Счетчик + 1;
НовыеТаблицыПроводок.Добавить(НоваяТаблицаПроводок);
КонецЦикла;
ОтменитьТранзакцию();
3) Обратно превратим сохранённые таблицы значений в готовые к перезаписи наборы записей регистров. В данном конкретном примере, в коллекции движений документа флаг "Записывать" взводится в "Истину" только для хозрасчётного регистра - но если убрать одну проверку, можно перезаписать все проводки документа.
//Заполним коллекцию движений документа
Для Каждого ТаблицаПроводок Из НовыеТаблицыПроводок Цикл
ТекущийНабор = ДокОбъект.Движения[ТаблицаПроводок.Номер];
ТекущийНабор.Прочитать();
Если ТаблицаПроводок.Таблица.Количество() > 0 //Новая таблица регистров НЕ пустая
ИЛИ ТекущийНабор.Количество() > 0 Тогда //Документ имел движения по регистру
ТекущийНабор.Загрузить(ТаблицаПроводок.Таблица);
//Если ТекущийНабор = ДокОбъект.Движения.Хозрасчетный - эта проверка ЧИСТО для примера, так как мы знаем, какой конкретно регистр мы поменяли
Если ТекущийНабор = ДокОбъект.Движения.Хозрасчетный Тогда
//ТекущийНабор.ОбменДанными.Загрузка = Истина; //ВАЖНО - у регистров тоже могут быть свои подписки на событие при записи и свои обработчики записи.
ТекущийНабор.Записывать = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
4) Переотразим конкретные движения, и в режиме обмена данных запишем в документ сообщение о том, что он был откорректирован.
НачатьТранзакцию();
ДокОбъект.Движения.Записать(); //Собственно запись движений
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.Комментарий = "#КорРег " + ДокОбъект.Комментарий;
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать(); //Мы делаем отметку, что мы успешно переотразили документ - это для красоты.
ЗафиксироватьТранзакцию();
В случае реальной базы алгоритм может работать несколько часов, но по прошествии этих нескольких часов программист получит переотражённые документы + список тех документов, которые не удалось переотразить (в реальной практике автора успешно отразилось всё на выборке с несколькими тысячами реализаций).
Пример работы обработки (в данном примере в запросе, формирующем выборку документов, установлено "ПЕРВЫЕ 10"):
Шаг 1: поменяли алгоритм отражения РТиУ, но НЕ перепровели документ.

Шаг 2: запустили обработку - она предупредила, что у нас могут быть не переотражённые документы, переотразила соответствующие документы и сообщила, сколько секунд она работала.

Шаг 3) Заходим в документ и видим, что его движения поменялись.

Замечание 1: поскольку перепроведение документа - "тяжёлая" операция, алгоритм заведомо непроизводителен по сравнению с обработчиком, который корректирует регистры напрямую, без записи документа сперва в режими проведения, а потом в режиме загрузки данных. Смысл обработки не столько в написании быстрого алгоритма, сколько в том, чтобы как можно надёжнее и универсальней гарантировать, что движения по регистру будут переотражены так, словно документ перепровели руками + показать, что такой приём в принципе возможен.
Замечание 2: у алгоритма есть неприятное ограничение - он принципиально неприменим для регистров, которые проводятся отложенно (это встречается, например, для хозрасчётного регистра в ERP).
Замечание 3: использованный трюк подразумевает, что документ можно было переписать и "вручную" - таким образом, нельзя, например, переотразить документы, запрещённые пользователю обработки через дату запрета изменения.
Замечание 4. ВАЖНО: в общем случае, программная обработка события ПриЗаписи может быть не только у документа, но и у набора записей - обратите внимание на модуль набора записей изменяемого регистра, а конкретно - есть ли у него обработчик события ПриЗаписи.
К статье прилагается файл с обработкой и её полный исходный код:
&НаСервере
Процедура ОтразитьНаСервереВариантСПроведениемРТУ()
Начало = ТекущаяДата(); //для удобного "замера производительности"
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 10 //Можно убрать ""Первые 10"", но на реальной базе это будет работать несколько часов, а обработка демонстрационная.
| Реализация.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата >= &Дата
| И НЕ (ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(7))) = ""#КорРег""
| И Реализация.Проведен
| И НЕ Реализация.РучнаяКорректировка
|
|УПОРЯДОЧИТЬ ПО
| Реализация.Дата,
| Реализация.Номер");
Дата = '202501010000';
Запрос.УстановитьПараметр("Дата", Дата);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Выгрузить().Количество() = 0 Тогда
ОбщегоНазначения.СообщитьПользователю("Мы всё переотразили! Ура!");
Иначе
ОбщегоНазначения.СообщитьПользователю("Обратите внимание - возможно не всё переотражено");
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
КоличествоРегистров = 0;
Пока Выборка.Следующий() Цикл
Попытка
НовыеТаблицыПроводок = Новый Массив;
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
//Это не совсем "групповое перепроведение", но это допсвойство позволит пропустить много "тяжёлых" операций.
ДокОбъект.ДополнительныеСвойства.Вставить("ГрупповоеПерепроведение", Истина);
//Создадим новые проводки документа - проще всего это сделать, проведя документ. Потом откатим транзакцию.
НачатьТранзакцию();
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Если КоличествоРегистров = 0 Тогда
КоличествоРегистров = ДокОбъект.Движения.Количество();
КонецЕсли;
Счетчик = 0;
Пока Счетчик < КоличествоРегистров Цикл
НоваяТаблицаПроводок = Новый Структура("Номер, Таблица");
НоваяТаблицаПроводок.Номер = Счетчик; //Синтакс-помощник не обещает, что при обходе движений документа обход будет упорядочен
НоваяТаблицаПроводок.Таблица = ДокОбъект.Движения[Счетчик].Выгрузить();
Счетчик = Счетчик + 1;
НовыеТаблицыПроводок.Добавить(НоваяТаблицаПроводок);
КонецЦикла;
ОтменитьТранзакцию();
//Заполним коллекцию движений документа
Для Каждого ТаблицаПроводок Из НовыеТаблицыПроводок Цикл
ТекущийНабор = ДокОбъект.Движения[ТаблицаПроводок.Номер];
ТекущийНабор.Прочитать();
Если ТаблицаПроводок.Таблица.Количество() > 0 //Новая таблица регистров НЕ пустая
ИЛИ ТекущийНабор.Количество() > 0 Тогда //Документ имел движения по регистру
ТекущийНабор.Загрузить(ТаблицаПроводок.Таблица);
//Если ТекущийНабор = ДокОбъект.Движения.Хозрасчетный - эта проверка ЧИСТО для примера, так как мы знаем, какой конкретно регистр мы поменяли
Если ТекущийНабор = ДокОбъект.Движения.Хозрасчетный Тогда
//ТекущийНабор.ОбменДанными.Загрузка = Истина; //ВАЖНО - у регистров тоже могут быть свои подписки на событие при записи и свои обработчики записи.
ТекущийНабор.Записывать = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
НачатьТранзакцию();
ДокОбъект.Движения.Записать(); //Собственно запись движений
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.Комментарий = "#КорРег " + ДокОбъект.Комментарий;
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать(); //Мы делаем отметку, что мы успешно переотразили документ - это для красоты.
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Ошибка = ОписаниеОшибки();
ОбщегоНазначения.СообщитьПользователю(Ошибка);
ОбщегоНазначения.СообщитьПользователю(СтрШаблон("Были проблемы на документе %1", Выборка.Ссылка), Выборка.Ссылка);
ЗаписьЖурналаРегистрации("рои_ПереотражениеДокументов", УровеньЖурналаРегистрации.Ошибка, , Выборка.Ссылка, СтрШаблон("Были проблемы на документе %1 ОШИБКА %2", Выборка.Ссылка, Ошибка));
КонецПопытки;
КонецЦикла;
Конец = ТекущаяДата(); //для удобного "замера производительности"
Сообщить(Конец - Начало);
КонецПроцедуры
&НаКлиенте
Процедура Отразить(Команда)
ОтразитьНаСервереВариантСПроведениемРТУ();
КонецПроцедуры
Проверено на следующих конфигурациях и релизах:
- Бухгалтерия предприятия КОРП, редакция 3.0, релизы 3.0.195.40
Вступайте в нашу телеграмм-группу Инфостарт