Каталог решений - Технологическое перепроведение документов

Технологическое перепроведение документов

Технологическое перепроведение документов

В наличии

Многие программисты и сисадмины, работающие с 1С 7.7 иногда ловят себя на мысли: как было бы здорово, если бы регистры можно было бы редактировать так же просто, как и бухгалтерские операции. Несколько лет назад эта проблема стала для меня настолько важной, что пришлось начать искать пути для её решения…

Категория:

Описание

При изучении материала Коррекция регистров вспомнился мой собственный опыт корректировки регистров уже проведенных документов.

В отличие от способа Владислава Цылёва, я не использовал корректирующие документы, так как исправлять нужно было сразу большую последовательность документов. Не пользовался я также и прямыми запросами – для этого мне элементарно не хватило знаний.

 Но всё по порядку:

Было это больше 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, которой пользуюсь уже много лет. (Выражаю огромную благодарность её автору).

Вот так в ней выглядит список ДвиженияРегистров. В качестве значений этого списка – таблицы, повторяющие структуру регистров и содержащие движения регистра документом.

 spisok

 

 

Вот пример таблицы ОстаткиТоваров. Её структура копирует структуру регистра ОстаткиТоваров, кроме двух служебных колонок: ВидДвижения, и НомерСтрокиДок.

ostatki 

 

Если документ имеет бухпроводки, то они записываются в таблицу с названием Операция.

operacia 

Для реквизитов СуммаОперации и Содержание в этой таблице места не нашлось, и они занимают «особое» положение в списке значений ДвижениеРегистров.

Заполнение таблицы Операция сделано для того, чтобы контролировать проводки во время технологического перепроведения, и не обрабатывать документ дважды, фактически двумя способами. Ведь если уж мы всё равно его перепроводим, зачем дополнительно править операцию?

В глобальный модуль также нужно поместить процедуру глТехнологическоеПерепроведение(), назначение которой – проводить документ «из таблицы».

Процедура глТехнологическоеПерепроведение(Конт, ДвиженияРегистров)Экспорт         

   Для ИндРег = 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»);  

   Сообщить(«»);

   Сигнал();

   Предупреждение(«Готово!»);

КонецПроцедуры 

 

Хочу отметить, что технологическое перепроведение проходит значительно быстрее обычного, т.к. предварительные расчеты не проводятся, а берутся сразу готовые результаты из таблицы.

 

Если требуется распечатать  движение регистра документа, можно использовать последовательность процедур глЗаполнитьДвижения() и глПоказать().

На основе этой методики мой бывший напарник сваял обработку для интерактивной правки движений документа:

obrabotka 

Чтобы при повторном проведении после исправления старые движения не восстанавливались, эту штуку использую совместно с обработкой Editrekv.ert Владислава Цилёва.

 

Ну вот наверное и всё, что я хотел сказать. Если будете использовать, то имейте ввиду, что при работе с бухпроводками процедуры глЗаполнитьДвижения() и глТехнологическоеПерепроведение () не сохраняют и не восстанавливают реквизиты Валюта, ВалСумма, Основание… Просто не используем мы их. Ну если до сюда дочитали, то добавить сами сможете 🙂

 

has been added to your cart:
Оформление заказа