В процессе разработки часто возникает необходимость программного дублирования движений документа. Казалось бы, всё просто — взять существующую запись регистра бухгалтерии и создать на её основе новую. Однако на практике программисты сталкиваются с рядом нюансов: от блокировки системных полей до некорректного копирования аналитики (субконто). В этой статье мы подробно разберем, как эффективно решить эту задачу, проанализируем различные подходы и выберем наиболее оптимальные инструменты.
Первый и самый распространенный способ — это использование промежуточной таблицы значений. Механика работы с данными в памяти здесь отдаленно напоминает добавление данных в существующую временную таблицу, но со своими особенностями. Проанализируем, почему нельзя просто добавить новую запись в набор и приравнять её к существующей. В регистре бухгалтерии есть системное поле НомерСтроки, которое управляется платформой автоматически. Если мы попытаемся скопировать его напрямую, система может выдать ошибку или проигнорировать запись.
Рассмотрим пошагово алгоритм через выгрузку в ТаблицаЗначений:
Посмотрим на пример кода, реализующий этот подход:
ДокОбъект = Док.ПолучитьОбъект();
Хозрасчетный = ДокОбъект.Движения.Хозрасчетный;
Хозрасчетный.Прочитать();
// Выгружаем движения в ТЗ для манипуляций
ТЗ = Хозрасчетный.Выгрузить();
// Добавляем новую строку, копируя все свойства из первой строки (индекс 0),
// при этом обязательно исключаем "НомерСтроки", чтобы платформа назначила его сама
ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), ТЗ[0], , "НомерСтроки");
// Загружаем измененную таблицу обратно в движения документа
Хозрасчетный.Загрузить(ТЗ);
Этот метод хорош тем, что ЗаполнитьЗначенияСвойств корректно переносит простые реквизиты: счет, сумму, количество, содержание. Однако он имеет ограничение при работе с субконто, если они не представлены в виде колонок в ТЗ.
Если нам нужно минимизировать количество операций и не плодить лишние объекты в памяти, мы можем работать непосредственно с коллекцией движений. Проанализируем ситуацию: метод Добавить() создает пустую запись, которую нам нужно заполнить по образу и подобию существующей.
Рассмотрим компактный вариант реализации:
// Предположим, ДокОбъект уже получен
ДвиженияХозрасчетный = ДокОбъект.Движения.Хозрасчетный;
ДвиженияХозрасчетный.Прочитать();
// Создаем новую проводку
НоваяПроводка = ДвиженияХозрасчетный.Добавить();
// Копируем данные из первой существующей проводки (индекс 0)
// Исключаем НомерСтроки, так как это системный идентификатор
ЗаполнитьЗначенияСвойств(НоваяПроводка, ДвиженияХозрасчетный[0], , "НомерСтроки");
Важное замечание: Несмотря на компактность, этот способ часто "спотыкается" на субконто. В 1С СубконтоДт и СубконтоКт — это не просто поля, а специальные коллекции (наборы записей). Прямое копирование через ЗаполнитьЗначенияСвойств не перенесет аналитику автоматически.
Выясним причину, по которой субконто не копируются обычными методами. Аналитика в регистре бухгалтерии хранится в подчиненной таблице. Когда мы обращаемся к Проводка.СубконтоДт, мы получаем объект типа РегистрБухгалтерииСубконто.Хозрасчетный.
Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), задача решается значительно проще. В модуле БухгалтерскийУчетДвижения (или аналогичных модулях бухгалтерского учета в БП 3.0) существуют вспомогательные функции. Для уверенной работы с ними рекомендуем использовать справочник по методам БСП.
Разберем пример с использованием функций БСП:
// Копирование субконто дебета из старой проводки в новую
БухгалтерскийУчет.СкопироватьСубконто(ИсточникПроводка.СчетДт, ИсточникПроводка.СубконтоДт,
НоваяПроводка.СчетДт, НоваяПроводка.СубконтоДт);
// Аналогично для кредита
БухгалтерскийУчет.СкопироватьСубконто(ИсточникПроводка.СчетКт, ИсточникПроводка.СубконтоКт,
НоваяПроводка.СчетКт, НоваяПроводка.СубконтоКт);
Если же БСП не используется, нам придется обходить коллекцию субконто в цикле, используя метод Вставить() или Установить() для каждого вида субконто, определенного на счете.
Рассмотрим ситуацию, когда нам нужно скопировать проводки в большом количестве документов или мы хотим избежать накладных расходов на создание объекта документа ПолучитьОбъект(). В этом случае эффективнее работать напрямую с регистром через СоздатьНаборЗаписей().
Проанализируем шаги:
Такой подход работает быстрее, так как не инициализирует тяжелые модули объекта документа и его экранные формы. Однако в сложных сценариях записи может потребоваться самодельный обработчик ПослеЗаписи, чтобы гарантировать корректное завершение транзакции.
Набор = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(СсылкаНаДокумент);
Набор.Прочитать();
ТЗ_Движений = Набор.Выгрузить();
// Копируем нужную строку
НоваяСтрока = ТЗ_Движений.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТЗ_Движений[0], , "НомерСтроки");
Набор.Загрузить(ТЗ_Движений);
Набор.Записать();
Проанализируем технические детали, которые могут привести к ошибкам, если их не учесть:
Активность = Ложь. Если вы копируете проводку, которая была "выключена" (например, сторно), новая проводка тоже будет неактивной.Период в движениях обычно должно совпадать с датой документа. При копировании убедитесь, что вы не перенесли старую дату в документ с новой датой — грамотная работа с датами в «1С:Предприятие» здесь критически важна.Подводя итог, можно сказать, что самый надежный способ копирования "в лоб" для программиста — это использование ТаблицыЗначений с четким исключением системных колонок и обязательным использованием специализированных методов для копирования аналитики (субконто). Это гарантирует целостность данных и корректную работу бухгалтерских отчетов.