При изучении материала Коррекция регистров вспомнился мой собственный опыт корректировки регистров уже проведенных документов.
В отличие от способа Владислава Цылёва, я не использовал корректирующие документы, так как исправлять нужно было сразу большую последовательность документов. Не пользовался я также и прямыми запросами – для этого мне элементарно не хватило знаний.
Но всё по порядку:
Было это больше 5 лет назад. Началось всё с того, что начальство поручило мне автоматизировать работу вновь созданного Отдела альтернативной подписки на базе нашей уже работающей программы (1С 7.7 Самописная конфигурация, Оперативный и Бухгалтерский учет). Дело было новое и незнакомое – как работает подписка и что требуется для этого, не знали не только я, но и руководство. Просто нас с начальником созданного отдела поставили перед фактом: Всё должно работать!
В процессе решения этой задачи, на начальном этапе, пока в наших головах не сложилось правильное представление о том, что нам нужно, мне приходилось многократно переделывать структуры регистров и документов, перепроводить документы в «боевой» базе, в том числе и в закрыто» периоде – резать, что называется, по живому. Бухгалтерия была недовольна и категорически запрещала нам пересчитывать итоги. Если проводки ещё можно было скорректировать, не перепроводя документы, то с регистрами я этого делать не мог.
Постепенно в голове сформировался механизм перепроведения документов без пересчёта регистров и проводок. Идея состояла в том, что нужно перед проведением документа считать его движения в таблицу, а при проведении из этой же таблицы загрузить. Между этими действиями, с таблицей можно делать всё что угодно: менять значения ячеек, пересчитывать выборочно её строки, добавлять туда движения новых регистров или удалять то, что перестало быть необходимым.
Я поделился своими мыслями с друзьями, и мне подсказали, что нечто подобное уже предлагалось фирмой 1С на одном из дисков ИТС. После непродолжительных поисков я нашёл пример под названием «Технологическое перепроведение». На его основе был создан и внедрен в нашей организации механизм «технологического перепроведения документов». Поскольку за основу были взяты разработки 1С, то никакого авторства я за собой не оставляю.
Для заполнения движений и проводок документа используется функция глобального модуля глЗаполнитьДвижения().
Функция глЗаполнитьДвижения(Документ) Экспорт
ДвиженияРегистров=СоздатьОбъект("СписокЗначений");
Если ПустоеЗначение(Документ) = 1 Тогда
Возврат 0;
КонецЕсли;
Док = СоздатьОбъект("Документ");
Док.НайтиДокумент(Документ);
Для ИндРег = 1 По Метаданные.Регистр() Цикл
МДРегистр = Метаданные.Регистр(ИндРег);
ДвиженияРегистра = СоздатьОбъект("ТаблицаЗначений");
ДвиженияРегистра.НоваяКолонка("ВидДвижения", "Строка", 3);
ДвиженияРегистра.НоваяКолонка("НомерСтрокиДок", "Число", 5);
Для Инд = 1 По МДРегистр.Измерение() Цикл
ДвиженияРегистра.НоваяКолонка(МДРегистр.Измерение(Инд).Идентификатор, МДРегистр.Измерение(Инд));
КонецЦикла;
Для Инд = 1 По МДРегистр.Ресурс() Цикл
ДвиженияРегистра.НоваяКолонка(МДРегистр.Ресурс(Инд).Идентификатор, МДРегистр.Ресурс(Инд));
КонецЦикла;
Для Инд = 1 По МДРегистр.Реквизит() Цикл
ДвиженияРегистра.НоваяКолонка(МДРегистр.Реквизит(Инд).Идентификатор, МДРегистр.Реквизит(Инд));
КонецЦикла;
Рег = СоздатьОбъект("Регистр." + МДРегистр.Идентификатор);
Рег.ВыбратьДвиженияДокумента(Док.ТекущийДокумент());
Пока Рег.ПолучитьДвижение() = 1 Цикл
ДвиженияРегистра.НоваяСтрока();
Стр = ДвиженияРегистра.КоличествоСтрок();
ДвиженияРегистра.ВидДвижения = ?(Рег.Приход = 1, "+", "-");
ДвиженияРегистра.НомерСтрокиДок = Рег.НомерСтроки();
Для Инд = 1 По МДРегистр.Измерение() Цикл
Имя = МДРегистр.Измерение(Инд).Идентификатор;
ДвиженияРегистра.УстановитьЗначение(Стр, Имя, Рег.ПолучитьАтрибут(Имя));
КонецЦикла;
Для Инд = 1 По МДРегистр.Ресурс() Цикл
Имя = МДРегистр.Ресурс(Инд).Идентификатор;
ДвиженияРегистра.УстановитьЗначение(Стр, Имя, Рег.ПолучитьАтрибут(Имя));
КонецЦикла;
Для Инд = 1 По МДРегистр.Реквизит() Цикл
Имя = МДРегистр.Реквизит(Инд).Идентификатор;
ДвиженияРегистра.УстановитьЗначение(Стр, Имя, Рег.ПолучитьАтрибут(Имя));
КонецЦикла;
КонецЦикла;
ДвиженияРегистров.Установить(МДРегистр.Идентификатор, ДвиженияРегистра);
КонецЦикла;
Операция = СоздатьОбъект("Операция");
Если Операция.НайтиОперацию(Документ) = 1 Тогда
ДвиженияРегистров.Установить("СуммаОперации", Операция.СуммаОперации);
ДвиженияРегистров.Установить("Содержание", СокрЛП(Операция.Содержание));
Проводки = СоздатьОбъект("ТаблицаЗначений");
Проводки.НоваяКолонка("Дт", "Счет",,,, 5);
Проводки.НоваяКолонка("СубкД1",,,,, 10);
Проводки.НоваяКолонка("СубкД2",,,,, 10);
Проводки.НоваяКолонка("СубкД3",,,,, 10);
Проводки.НоваяКолонка("Кт", "Счет",,,, 5);
Проводки.НоваяКолонка("СубкК1",,,,, 10);
Проводки.НоваяКолонка("СубкК2",,,,, 10);
Проводки.НоваяКолонка("СубкК3",,,,, 10);
Проводки.НоваяКолонка("Количество", "Число", 5,,, 3);
Проводки.НоваяКолонка("Сумма", "Число", 15, 2,, 6);
Проводки.НоваяКолонка("СодержаниеПроводки", "Строка",,,, 10);
Проводки.НоваяКолонка("НомерЖурнала", "Строка",,,, 4);
Операция.ВыбратьПроводки();
Пока Операция.ПолучитьПроводку() = 1 Цикл
Проводки.НоваяСтрока();
ТекСтр = Проводки.НомерСтроки;
ОперДбСчет = Операция.Дебет.Счет;
Проводки.Дт = ОперДбСчет;
Если ОперДбСчет.КоличествоСубконто() > 0 Тогда
Для ъ = 1 по ОперДбСчет.КоличествоСубконто() Цикл
Проводки.УстановитьЗначение(ТекСтр, "СубкД" + ъ, Операция.Дебет.Субконто(ъ));
КонецЦикла;
КонецЕсли;
ОперКтСчет = Операция.Кредит.Счет;
Проводки.Кт = ОперКтСчет;
Если ОперКтСчет.КоличествоСубконто() > 0 Тогда
Для ъ = 1 по ОперКтСчет.КоличествоСубконто() Цикл
Проводки.УстановитьЗначение(ТекСтр, "СубкК" + ъ, Операция.Кредит.Субконто(ъ));
КонецЦикла;
КонецЕсли;
Проводки.Количество = Операция.Количество;
Проводки.Сумма = Операция.Сумма;
Проводки.СодержаниеПроводки = Операция.СодержаниеПроводки;
Проводки.НомерЖурнала = Операция.НомерЖурнала;
КонецЦикла;
ДвиженияРегистров.Установить("Операция", Проводки);
КонецЕсли;
ДвиженияРегистров.Установить("Вариант", "ТехнологическоеПерепроведение");
Возврат ДвиженияРегистров;
КонецФункции
Для демонстрации результата выполнения функции я использую разработку глПоказать() многоуважаемого zorro, которой пользуюсь уже много лет. (Выражаю огромную благодарность её автору).
Вот так в ней выглядит список ДвиженияРегистров. В качестве значений этого списка – таблицы, повторяющие структуру регистров и содержащие движения регистра документом.
Вот пример таблицы ОстаткиТоваров. Её структура копирует структуру регистра ОстаткиТоваров, кроме двух служебных колонок: ВидДвижения, и НомерСтрокиДок.
Если документ имеет бухпроводки, то они записываются в таблицу с названием Операция.
Для реквизитов СуммаОперации и Содержание в этой таблице места не нашлось, и они занимают «особое» положение в списке значений ДвижениеРегистров.
Заполнение таблицы Операция сделано для того, чтобы контролировать проводки во время технологического перепроведения, и не обрабатывать документ дважды, фактически двумя способами. Ведь если уж мы всё равно его перепроводим, зачем дополнительно править операцию?
В глобальный модуль также нужно поместить процедуру глТехнологическоеПерепроведение(), назначение которой – проводить документ «из таблицы».
Процедура глТехнологическоеПерепроведение(Конт, ДвиженияРегистров)Экспорт
Для ИндРег = 1 По Метаданные.Регистр() Цикл
МДРегистр = Метаданные.Регистр(ИндРег);
ДвиженияРегистра = ДвиженияРегистров.Получить(МДРегистр.Идентификатор);
Если ПустоеЗначение(ДвиженияРегистра) = 1 Тогда
Продолжить;
КонецЕсли;
Рег = Конт.Регистр.ПолучитьАтрибут(МДРегистр.Идентификатор);
Для Стр = 1 По ДвиженияРегистра.КоличествоСтрок() Цикл
Рег.ПривязыватьСтроку(ДвиженияРегистра.ПолучитьЗначение(Стр, "НомерСтрокиДок"));
Для Инд = 1 По МДРегистр.Измерение() Цикл
Имя = МДРегистр.Измерение(Инд).Идентификатор;
ПрисвоитьЗначениеСТипом(Рег, Имя, МДРегистр.Измерение(Инд), ДвиженияРегистра.ПолучитьЗначение(Стр, Имя));
КонецЦикла;
Для Инд = 1 По МДРегистр.Ресурс() Цикл
Имя = МДРегистр.Ресурс(Инд).Идентификатор;
Рег.УстановитьАтрибут(Имя, ДвиженияРегистра.ПолучитьЗначение(Стр, Имя));
КонецЦикла;
Для Инд = 1 По МДРегистр.Реквизит() Цикл
Имя = МДРегистр.Реквизит(Инд).Идентификатор;
ПрисвоитьЗначениеСТипом(Рег, Имя, МДРегистр.Реквизит(Инд), ДвиженияРегистра.ПолучитьЗначение(Стр, Имя));
КонецЦикла;
Если МДРегистр.ТипРегистра = "Остатки" Тогда
Если СокрЛП(ДвиженияРегистра.ПолучитьЗначение(Стр, "ВидДвижения")) = "+" Тогда
Рег.ДвижениеПриходВыполнить();
Иначе
Рег.ДвижениеРасходВыполнить();
КонецЕсли;
Иначе
Рег.ДвижениеВыполнить();
КонецЕсли;
КонецЦикла;
КонецЦикла;
Если ТипЗначенияСтр(ДвиженияРегистров.Получить("Операция")) = "ТаблицаЗначений" Тогда
Опер = Конт.Операция;
ДвиженияРегистра = ДвиженияРегистров.Получить("Операция");
Для Стр = 1 По ДвиженияРегистра.КоличествоСтрок() Цикл
Опер.НоваяПроводка();
Опер.Дебет.Счет = ДвиженияРегистра.ПолучитьЗначение(Стр, "Дт");
Если Опер.Дебет.Счет.КоличествоСубконто() > 0 Тогда
Для ъ = 1 по Опер.Дебет.Счет.КоличествоСубконто() Цикл
СубкД = ДвиженияРегистра.ПолучитьЗначение(Стр, "СубкД" + ъ);
Опер.Дебет.Субконто(ъ, СубкД);
КонецЦикла;
КонецЕсли;
Опер.Кредит.Счет = ДвиженияРегистра.ПолучитьЗначение(Стр, "Кт");
Если Опер.Кредит.Счет.КоличествоСубконто() > 0 Тогда
Для ъ = 1 по Опер.Кредит.Счет.КоличествоСубконто() Цикл
СубкК = ДвиженияРегистра.ПолучитьЗначение(Стр, "СубкК" + ъ);
Опер.Кредит.Субконто(ъ, СубкК);
КонецЦикла;
КонецЕсли;
Если ПустоеЗначение(ДвиженияРегистра.ПолучитьЗначение(Стр, "Количество")) = 0 Тогда
Опер.Количество = ДвиженияРегистра.ПолучитьЗначение(Стр, "Количество");
КонецЕсли;
Опер.Сумма = ДвиженияРегистра.ПолучитьЗначение(Стр, "Сумма");
Опер.СодержаниеПроводки = ДвиженияРегистра.ПолучитьЗначение(Стр, "СодержаниеПроводки");
Если ПустоеЗначение(ДвиженияРегистра.ПолучитьЗначение(Стр, "НомерЖурнала")) = 0 Тогда
Опер.НомерЖурнала = ДвиженияРегистра.ПолучитьЗначение(Стр, "НомерЖурнала");
КонецЕсли;
КонецЦикла;
Опер.СуммаОперации = ДвиженияРегистров.Получить("СуммаОперации");
Опер.Содержание = ДвиженияРегистров.Получить("Содержание");
Опер.Записать();
КонецЕсли;
КонецПроцедуры
В обработки проведения документов нужно добавить следующие строки:
Процедура ОбработкаПроведения(Реж)
Если ТипЗначенияСтр(Реж) = "СписокЗначений" Тогда
Если Реж.Получить("Вариант") = "ТехнологическоеПерепроведение" Тогда
глТехнологическоеПерепроведение(Контекст, Реж);
Возврат;
КонецЕсли;
КонецЕсли;
…
Теперь если при проведении документа «подсунуть» ему в качестве параметра список с движениями:
Док.Провести(0, ДвиженияРегистров);
, то с этими движениями он и проведётся.
Вот собственно говоря и всё. Хотя отдел альтернативной подписки успешно работает и «подписная компонента» в корпоративной базе 1С «функцикулирует», от использования технологического перепроведения мы не отказались. Периодически бывает нужно скорректировать движения документа вслед за менеджерами.
Вот пример обработки, которую я использую, если они оприходовали не тот товар (и почему такие случаи выявляются только через полгода?):
Процедура Выполнить()
Если ВыбТов2=ВыбТов1 Тогда
Сигнал(); Предупреждение("Товары не должны быть одинаковыми!",5);
Возврат;
КонецЕсли;
Сообщить("Время начала обработки: " + ТекущееВремя(), "i");
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"Период с НачДата по КонДата;
|Товар = Регистр.ОстаткиТоваров.Товар;
|Док = Регистр.ОстаткиТоваров.ТекущийДокумент;
|Кол = Регистр.ОстаткиТоваров.Количество;
|Функция КолПриход = Приход(Кол);
|Функция КолРасход = Расход(Кол);
|Группировка Док;
|Условие(Товар = ВыбТов1);";
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Предупреждение("Не вышло?"); Возврат;
КонецЕсли;
Й=0;
Пока Запрос.Группировка(1) = 1 Цикл
Зап=0;
ДД = Запрос.Док; ДДВид=ДД.Вид();
ТД = СоздатьОбъект("Документ." + ДДВид);
ТД.НайтиДокумент(ДД);
Если ТД.Проведен() = 1 Тогда
ДвиженияРегистров = глЗаполнитьДвижения(ТД.ТекущийДокумент(), 1);
Состояние(""+ТД);
Если (ДДВид="НакладнаяПередачи") Тогда
ТЗ=ДвиженияРегистров.Получить("ПередачаТовара");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
Если ТЗ.Товар=ВыбТов1 Тогда
ТЗ.Товар=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если (ДДВид="ВозвратПоставщику") Тогда
ТЗ=ДвиженияРегистров.Получить("Поставщики");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
Если ТЗ.Товар=ВыбТов1 Тогда
ТЗ.Товар=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если (ДДВид="РасходнаяНакладная") Или (ДДВид="ВозвратнаяНакладная") Или (ДДВид="ПередачаТовара") Тогда
ТЗ=ДвиженияРегистров.Получить("Оборот");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
Если ТЗ.Товар=ВыбТов1 Тогда
ТЗ.Товар=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ТЗ=ДвиженияРегистров.Получить("ОстаткиТоваров");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
Если ТЗ.Товар=ВыбТов1 Тогда
ТЗ.Товар=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЦикла;
ТЗ=ДвиженияРегистров.Получить("Операция");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл
Если Лев(ТЗ.Дт,3)="41." Тогда
Если ТЗ.СубкД1=ВыбТов1 Тогда
ТЗ.СубкД1=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЕсли;
Если Лев(ТЗ.Кт,3)="41." Тогда
Если ТЗ.СубкК1=ВыбТов1 Тогда
ТЗ.СубкК1=ВыбТов2;
Зап=1;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если Зап=1 Тогда
Попытка
Й=Й+1;
Сообщить(" "+Й+". " + ТД, "i");
Если ТД.Провести(0, ДвиженияРегистров) = 0 Тогда
ОтменитьТранзакцию();
Сообщить("- Документ не перепроведён " + ДД, "!");
Прервать;
КонецЕсли;
Исключение
Сообщить("-- " + ОписаниеОшибки(), "!!!");
Прервать;
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Сообщить("Обработка завершена: " + ТекущееВремя() + " " + РазделительСтраниц, "i");
Сообщить("");
Сигнал();
Предупреждение("Готово!");
КонецПроцедуры
Хочу отметить, что технологическое перепроведение проходит значительно быстрее обычного, т.к. предварительные расчеты не проводятся, а берутся сразу готовые результаты из таблицы.
Если требуется распечатать движение регистра документа, можно использовать последовательность процедур глЗаполнитьДвижения() и глПоказать().
На основе этой методики мой бывший напарник сваял обработку для интерактивной правки движений документа:
Чтобы при повторном проведении после исправления старые движения не восстанавливались, эту штуку использую совместно с обработкой Editrekv.ert Владислава Цилёва.
Ну вот наверное и всё, что я хотел сказать. Если будете использовать, то имейте ввиду, что при работе с бухпроводками процедуры глЗаполнитьДвижения() и глТехнологическоеПерепроведение () не сохраняют и не восстанавливают реквизиты Валюта, ВалСумма, Основание… Просто не используем мы их. Ну если до сюда дочитали, то добавить сами сможете :-)