К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как скопировать запись регистра сведений программно в 1С

Как скопировать запись регистра сведений программно в 1С: менеджер записи, набор записей, изменение измерений, типичные ошибки и готовые примеры кода.

Копирование записи регистра сведений — типовая задача при разработке на платформе 1С:Предприятие: дублирование цен на новый период, перенос настроек между элементами справочника, создание записи по шаблону. В отличие от справочников, у регистра сведений нет метода «Скопировать()», поэтому запись создают вручную: читают существующую, переносят значения полей и записывают новую с изменёнными ключевыми полями.

Способ копирования зависит от режима регистра. Для независимого регистра сведений работают через менеджер записи или набор записей с отбором. Для регистра, подчинённого регистратору, запись существует только внутри движений документа, и скопировать её «сама по себе» нельзя — нужно копировать движения документа или создавать новый документ-регистратор.

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

Как устроена запись регистра сведений и почему нет готового метода копирования

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

Объектная модель предлагает два инструмента. Менеджер записи (объект типа РегистрСведенийМенеджерЗаписи) работает с одной строкой и подходит для независимых регистров. Набор записей (РегистрСведенийНаборЗаписей) работает с коллекцией строк и обязателен для регистров, подчинённых регистратору, а также удобен при массовом копировании.

Копирование одной записи независимого регистра через менеджер записи

Базовый сценарий: есть запись, её нужно продублировать с новым значением измерения — например, скопировать настройки с одной номенклатуры на другую. Сначала читают исходную запись, затем создают новую и переносят поля.

// Читаем исходную запись

ИсходнаяЗапись = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();

ИсходнаяЗапись.Номенклатура = ИсходнаяНоменклатура;

ИсходнаяЗапись.ВидЦены = ВидЦены;

ИсходнаяЗапись.Прочитать();

Если ИсходнаяЗапись.Выбран() Тогда

// Создаём новую запись — копию с другим измерением

НоваяЗапись = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();

ЗаполнитьЗначенияСвойств(НоваяЗапись, ИсходнаяЗапись);

НоваяЗапись.Номенклатура = НоваяНоменклатура;

НоваяЗапись.Записать();

КонецЕсли;

Ключевой приём — ЗаполнитьЗначенияСвойств(НоваяЗапись, ИсходнаяЗапись): он переносит все поля, включая ресурсы и реквизиты, без ручного перечисления. После этого обязательно переопределяют ключевые поля (измерения, период), иначе новая запись совпадёт по ключу с исходной и просто перезапишет её — копии не получится.

Метод Выбран() после Прочитать() показывает, найдена ли запись. Без этой проверки при отсутствии исходной записи вы создадите «копию» из пустых значений, что приведёт к мусорной строке в регистре.

Порядок копирования через менеджер записи
  1. 1
    Создайте менеджер записи и заполните значения измерений исходной записи
  2. 2
    Вызовите Прочитать() и проверьте результат через Выбран()
  3. 3
    Создайте второй менеджер записи для новой строки
  4. 4
    Перенесите поля через ЗаполнитьЗначенияСвойств
  5. 5
    Измените ключевые поля: измерения или период
  6. 6
    Вызовите Записать() и проверьте результат запросом или в форме списка

Копирование записи периодического регистра сведений

Для периодического регистра в ключ входит поле Период. Типовой сценарий — продлить значение на новую дату: например, перенести курс валюты или кадровые данные на следующий месяц. Логика та же, но при чтении указывают период, а при записи задают новый.

МенеджерЗаписи = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();

МенеджерЗаписи.Период = НачалоДня(ИсходнаяДата);

МенеджерЗаписи.Валюта = Валюта;

МенеджерЗаписи.Прочитать();

Если МенеджерЗаписи.Выбран() Тогда

Копия = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();

ЗаполнитьЗначенияСвойств(Копия, МенеджерЗаписи);

Копия.Период = НачалоДня(НоваяДата);

Копия.Записать();

КонецЕсли;

Обратите внимание на нормализацию периода: если регистр ведётся «в пределах дня», лучше явно приводить дату к началу дня через НачалоДня(). Иначе запись с временем 10:30:15 не совпадёт по ключу с записью на 00:00:00, и вы получите неожиданный дубль или «не найденную» запись при чтении.

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

Копирование через набор записей и массовое дублирование

Когда копировать нужно много записей — например, все цены одного вида цен на другой — по одной записи через менеджер работать медленно: каждая операция Записать() идёт отдельным обращением к базе. Эффективнее прочитать записи набором и сформировать новый набор.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ *

|ИЗ РегистрСведений.ЦеныНоменклатуры

|ГДЕ ВидЦены = &СтарыйВидЦены";

Запрос.УстановитьПараметр("СтарыйВидЦены", СтарыйВидЦены);

Выборка = Запрос.Выполнить().Выбрать();

НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();

НаборЗаписей.Отбор.ВидЦены.Установить(НовыйВидЦены);

Пока Выборка.Следующий() Цикл

Строка = НаборЗаписей.Добавить();

ЗаполнитьЗначенияСвойств(Строка, Выборка);

Строка.ВидЦены = НовыйВидЦены;

КонецЦикла;

НаборЗаписей.Записать();

Здесь важно понимать семантику набора: НаборЗаписей.Записать() полностью замещает все записи, попадающие под установленный отбор. Если по новому виду цен уже были данные, они будут удалены и заменены содержимым набора. Если нужно только добавить записи, не трогая существующие, используйте НаборЗаписей.Записать(Ложь) — режим добавления без замещения. В этом режиме конфликт по ключу (такая запись уже есть) вызовет ошибку, поэтому дубликаты нужно отфильтровать заранее.

Копирование записей регистра, подчинённого регистратору

Если регистр сведений подчинён регистратору, отдельной «записи» вне документа не существует: строки живут внутри движений документа. Скопировать такую запись напрямую менеджером записи нельзя — у подчинённого регистра этот объект недоступен. Работают с набором записей, привязанным к регистратору.

Типовая задача — продублировать движения одного документа в другой документ того же вида. Набор читают с отбором по исходному регистратору, переносят строки в движения нового документа и записывают:

// Источник

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

НаборИсточник.Отбор.Регистратор.Установить(СтарыйДокумент);

НаборИсточник.Прочитать();

// Приёмник — движения нового документа

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

НаборПриемник.Отбор.Регистратор.Установить(НовыйДокумент);

Для Каждого Строка Из НаборИсточник Цикл

НоваяСтрока = НаборПриемник.Добавить();

ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);

КонецЦикла;

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

Альтернатива — скопировать сам документ методом ДокументОбъект.Скопировать() и провести копию: тогда движения сформируются штатно при проведении. Этот путь предпочтительнее, если движения зависят от логики проведения, а не являются точной копией табличной части.

Сравнение способов копирования

Менеджер записи

Подходит для одной записи независимого регистра. Простой код, но отдельное обращение к базе на каждую строку — медленно при массовой обработке.

Набор записей с отбором

Оптимален для пакетного копирования и для регистров, подчинённых регистратору. Требует аккуратности с отбором: запись набора замещает данные под отбором.

Запрос + создание записей

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

Копирование документа-регистратора

Для подчинённого регистра, когда движения должны формироваться штатной логикой проведения. Не подходит, если нужна точная побитовая копия строк.

Проверка результата и типичные ошибки

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

Контрольные точки

Как проверить результат

Существование копии
Норма

Запрос по новым ключевым полям возвращает одну строку с нужными ресурсами

Тревожный признак

Запрос пуст — запись не создана, проверяйте отборы и права

Целостность исходной записи
Норма

Исходная строка на месте с прежними значениями

Тревожный признак

Исходная запись изменилась — ключи копии не были переопределены

Количество записей после массового копирования
Норма

Число строк под новым отбором равно числу скопированных

Тревожный признак

Строк меньше — часть потеряна из-за конфликтов ключей или Записать(Ложь) с дублями

Чаще всего ошибки сводятся к нескольким сценариям. Первая — забыли изменить ключевые поля после ЗаполнитьЗначенияСвойств: новая запись совпала по ключу с исходной и перезаписала её. Вторая — чтение без проверки Выбран(): при отсутствии исходной записи в регистр попадает строка с пустыми ресурсами. Третья — запись набора без отбора: для независимого регистра это попытка заместить всю таблицу. Четвёртая — игнорирование прав: у пользователя может не быть права на добавление или изменение регистра, тогда Записать() вызовет исключение.

Типичные ошибки
01
Не изменили измерения после ЗаполнитьЗначенияСвойств — копия затёрла исходную запись
02
Прочитать() без проверки Выбран() — в регистре появилась пустая мусорная строка
03
Записать() набора без отбора или с неверным отбором — замещены лишние записи, вплоть до очистки регистра
04
Игнорирование периода у периодического регистра — запись ушла на пустую дату или не находится при чтении
05
Попытка использовать менеджер записи для регистра, подчинённого регистратору — объект недоступен, нужен набор записей

Ограничения и безопасная работа в транзакции

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

Учитывайте подписки на события и обмены данными. Запись регистра вызывает обработчики ПередЗаписью и ПриЗаписи, в которых может быть бизнес-логика или регистрация изменений для обмена. При служебном переносе данных иногда оправдан НаборЗаписей.ОбменДанными.Загрузка = Истина — запись без контролей и триггеров, но это отключает и проверки целостности, поэтому применяют режим осознанно и только для технических операций.

Наконец, помните про блокировки: массовая запись в регистр, активно используемый пользователями (цены, курсы), может вызвать конфликты. Большие пакеты безопаснее выполнять вне пиковой нагрузки и дробить на порции.

Итог

Что важно запомнить

Копия записи регистра сведений — это новая запись с изменёнными ключевыми полями: читаем исходную, переносим значения через ЗаполнитьЗначенияСвойств, меняем измерения или период, записываем. Для независимого регистра используем менеджер записи или набор, для подчинённого — только набор записей с отбором по регистратору. Перед массовыми операциями проверяйте отбор и делайте резервную копию.

Частые вопросы

Можно ли скопировать запись регистра сведений одной строкой кода?

Полностью — нет, метода «Скопировать» у записей регистра нет. Минимальный вариант: прочитать менеджер записи, создать второй, перенести поля через ЗаполнитьЗначенияСвойств, изменить ключ и записать. Это 6–8 строк кода.

Почему после копирования исходная запись пропала?

Потому что новая запись получила тот же набор измерений (и периода), что и исходная, и заместила её. После ЗаполнитьЗначенияСвойств обязательно переопределите хотя бы одно ключевое поле — измерение, период или регистратор.

Как скопировать только актуальные (последние) значения периодического регистра?

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

Чем отличается Записать() от Записать(Ложь) у набора записей?

Записать() без параметра замещает все записи под установленным отбором содержимым набора. Записать(Ложь) только добавляет строки, не трогая существующие, но при совпадении ключа с уже существующей записью возникнет ошибка.

Как скопировать запись регистра, подчинённого регистратору?

Напрямую — никак, менеджер записи для такого регистра недоступен. Прочитайте набор записей с отбором по исходному регистратору, перенесите строки в набор с отбором по новому регистратору и запишите, либо скопируйте сам документ и проведите его.

Нужно ли отключать подписки на события при служебном копировании?

Если подписки содержат бизнес-логику, которая не должна срабатывать при техническом переносе, используйте ОбменДанными.Загрузка = Истина у набора записей. Учтите: это отключает все контроли и регистрацию изменений, поэтому применяйте режим только для осознанных сервисных операций.