Как программно сменить регистратор у записей регистра бухгалтерии и сохранить аналитику

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет
← На главную

В практике разработчика 1С часто возникает задача «перевесить» движения (проводки) с одного документа на другой. Это может потребоваться при исправлении ошибок ввода, объединении документов или реализации специфических механизмов корректировки данных. Однако простая попытка изменить поле Регистратор в цикле по набору записей приводит к ошибкам или потере данных. Рассмотрим подробнее, почему так происходит и как правильно решить эту задачу.

Почему не работает прямое изменение регистратора

Разберем типичную ошибку начинающего разработчика. Попытка программно изменить регистратор непосредственно «внутри» прочитанного набора записей выглядит логично, но технически невыполнима. Проанализируем следующий код:


НЗИсточник = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НЗИсточник.Отбор.Регистратор.Установить(РегистраторИсточник);
НЗИсточник.Прочитать();

Для Каждого Строка Из НЗИсточник Цикл 
    Строка.Регистратор = РегистраторПриемник;
КонецЦикла;

НЗИсточник.Записать();

Данный вариант не сработает по фундаментальной причине: набор записей жестко связан со своим отбором. Когда мы устанавливаем отбор по РегистраторИсточник, система ожидает, что в этом наборе будут находиться только те записи, у которых регистратор совпадает со значением отбора. Как только мы меняем Регистратор в строке на РегистраторПриемник, запись перестает соответствовать фильтру набора. При вызове метода Записать() платформа либо проигнорирует измененные записи, либо вызовет исключение, так как поле Регистратор является частью уникального ключа записи в базе данных.

Проблема потери аналитики (субконто) при копировании

Вторая распространенная ошибка — попытка скопировать записи из одного набора в другой через метод ЗаполнитьЗначенияСвойств. Рассмотрим ситуацию:


НЗПриемник = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НЗПриемник.Отбор.Регистратор.Установить(РегистраторПриемник);

НЗИсточник = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НЗИсточник.Отбор.Регистратор.Установить(РегистраторИсточник);
НЗИсточник.Прочитать();

Для Каждого Строка Из НЗИсточник Цикл 
    НоваяСтрокаПриемник = НЗПриемник.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемник, Строка);
    НоваяСтрокаПриемник.Регистратор = РегистраторПриемник;
КонецЦикла;

НЗПриемник.Записать();

При таком подходе программист обнаруживает, что проводки перенеслись, но вся аналитика (субконто) пропала. Выясним причину: в регистрах бухгалтерии СубконтоДт и СубконтоКт не являются обычными полями. Это специальные коллекции (объекты типа РегистрБухгалтерииСубконто), которые хранят связь вида субконто и его значения. Метод ЗаполнитьЗначенияСвойств не умеет глубоко копировать такие коллекции, он просто пропускает их, оставляя аналитику новой записи пустой.

Правильный алгоритм переноса записей с сохранением субконто

Чтобы корректно сменить регистратор, сохранив все данные, нам необходимо выполнить следующие шаги:

  1. Создать набор записей для документа-приемника и прочитать его (чтобы не затереть уже существующие там проводки, если они есть).
  2. Создать набор записей для документа-источника и прочитать его.
  3. В цикле перенести данные, используя специализированные методы для работы с субконто.
  4. Записать новый набор.
  5. Обязательно очистить и записать набор источника, чтобы избежать дублирования данных.

Посмотрим на пример реализации, который учитывает особенности типовых конфигураций (использование общего модуля БухгалтерскийУчет):


// 1. Подготовка набора-приемника
НЗПриемник = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НЗПриемник.Отбор.Регистратор.Установить(РегистраторПриемник);
НЗПриемник.Прочитать();

// 2. Подготовка набора-источника
НЗИсточник = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НЗИсточник.Отбор.Registor.Установить(РегистраторИсточник);
НЗИсточник.Прочитать();

// 3. Перенос данных в цикле
Для Каждого Строка Из НЗИсточник Цикл 
    НоваяСтрокаПриемник = НЗПриемник.Добавить();
    
    // Копируем основные реквизиты (Счета, Суммы, Количество)
    ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемник, Строка);   
    НоваяСтрокаПриемник.Регистратор = РегистраторПриемник;   
    
    // Корректный перенос подразделений (если используются)
    БухгалтерскийУчет.УстановитьПодразделенияПроводки(НоваяСтрокаПриемник, Строка.ПодразделениеДт, Строка.ПодразделениеКт);   
    
    // Перенос субконто по Дебету
    Для х = 1 По Строка.СчетДт.ВидыСубконто.Количество() Цикл
        ВидСубк = Строка.СчетДт.ВидыСубконто[х-1].ВидСубконто;
        ЗначениеСубк = Строка.СубконтоДт[ВидСубк];
        БухгалтерскийУчет.УстановитьСубконто(НоваяСтрокаПриемник.СчетДт, НоваяСтрокаПриемник.СубконтоДт, х, ЗначениеСубк);
    КонецЦикла;    
    
    // Перенос субконто по Кредиту
    Для х = 1 По Строка.СчетКт.ВидыСубконто.Количество() Цикл
        ВидСубк = Строка.СчетКт.ВидыСубконто[х-1].ВидСубконто;
        ЗначениеСубк = Строка.СубконтоКт[ВидСубк];
        БухгалтерскийУчет.УстановитьСубконто(НоваяСтрокаПриемник.СчетКт, НоваяСтрокаПриемник.СубконтоКт, х, ЗначениеСубк);
    КонецЦикла;    
КонецЦикла;

// 4. Сохранение изменений в приемнике
НЗПриемник.Записать();

// 5. Очистка старого регистратора
НЗИсточник.Очистить();
НЗИсточник.Записать();

Альтернативный метод через Таблицу Значений

Проанализируем еще один способ, который может быть более производительным при большом объеме записей. Вместо построчного добавления в цикле, можно использовать выгрузку в промежуточную таблицу.

Метод Выгрузить() набора записей регистра бухгалтерии формирует таблицу значений, где субконто представлены отдельными колонками вида СубконтоДт1, СубконтоКт1 и так далее. Если структуры планов счетов в обоих случаях идентичны, мы можем выполнить операцию быстрее:

  1. Выгружаем набор источника в таблицу значений: ТЗ = НЗИсточник.Выгрузить();
  2. Массово меняем значение в колонке Регистратор для всех строк таблицы.
  3. Загружаем таблицу в набор приемника: НЗПриемник.Загрузить(ТЗ);

Важное примечание: Метод с Загрузить() следует использовать осторожно. Если записи в приемнике уже были, они будут полностью заменены данными из таблицы. Также нужно быть уверенным, что индексы субконто в таблице соответствуют настройкам счетов в новом наборе.

Дополнительные рекомендации

При выполнении подобных операций важно учитывать следующие моменты:

Транзакционность: Всегда оборачивайте процесс переноса в транзакцию (используйте НачатьТранзакцию() и ЗафиксироватьТранзакцию()). Это гарантирует, что если в процессе записи приемника произойдет сбой, записи в источнике не будут удалены, и данные не «повиснут» в неопределенном состоянии.

Блокировки: Регистр бухгалтерии — это одна из самых нагруженных таблиц базы данных. Программная перезапись наборов вызывает блокировку таблиц итогов. Выполняйте такие процедуры в периоды минимальной активности пользователей.

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

Таким образом, мы выяснили, что ключ к успешной смене регистратора заключается в понимании структуры коллекции Субконто и правильном управлении наборами записей. Использование специализированных методов для установки аналитики гарантирует целостность бухгалтерских данных при любых программных манипуляциях.

← На главную