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

· 9 мин

Содержание

Перезаписать запись регистра сведений в 1С 8.3 можно двумя объектами: для независимого регистра — менеджером записи (СоздатьМенеджерЗаписи), для подчинённого регистратору — набором записей (СоздатьНаборЗаписей) с методом Записать(Истина). Параметр Истина означает «заместить»: старая запись с тем же ключом измерений исчезает, на её место встаёт новая.

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

Как регистр хранит данные и почему это важно для записи

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

Два свойства регистра определяют способ записи:

  • 🔹 Режим записи «Независимый» — записи живут сами по себе, регистратора у них нет. Пишутся менеджером записи или независимым набором записей.
  • 🔹 Режим записи «Подчинение регистратору» — каждая запись привязана к документу-регистратору. Пишутся только набором записей с отбором по регистратору.
  • 🔹 Периодичность — если регистр периодический, в ключ добавляется измерение Период. Без заполненного периода запись в периодический регистр не уйдёт.

Если не знаете режим и периодичность конкретного регистра — не гадайте. Откройте дерево конфигурации, найдите регистр в ветке «Регистры сведений» и посмотрите его свойства. От этого зависит, какой из примеров ниже вам подходит.

Выбрать способ помогает короткая схема.

Каким объектом перезаписывать регистр сведений?

Перезапись независимого регистра менеджером записи

Менеджер записи работает с одной записью по полному набору измерений. Алгоритм — задать ключ, задать ресурсы, записать.

Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
Менеджер.Период        = ТекущаяДата();      // только для периодического регистра
Менеджер.Номенклатура  = ВыбраннаяНоменклатура;
Менеджер.ТипЦен        = ВыбранныйТипЦен;
Менеджер.Цена          = 1500;
Менеджер.Записать(Истина);                   // Истина — заместить существующую запись

Параметр метода Записать управляет поведением при коллизии ключа:

ЗначениеЧто делает платформа
Истиназапись с таким ключом замещается; если её не было — добавляется
Ложьесли запись с таким ключом уже есть, будет вызвано исключение

Для «честной» перезаписи почти всегда нужен Истина. Ложь берут, когда нужно гарантированно добавить новую запись и упасть, если дубль по ключу уже существует.

Если новое значение зависит от старого (например, поднять цену на 10%), сначала прочитайте запись:

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

Если Менеджер.Выбран() Тогда
    Менеджер.Цена = Менеджер.Цена * 1.1;
    Менеджер.Записать(Истина);
КонецЕсли;

Прочитать() подтянет запись по заданным измерениям, Выбран() покажет, нашлась ли она. Вызывать Записать без проверки Выбран() после чтения опасно: если записи не было, вы перезапишете её значениями по умолчанию.

Перезапись независимого регистра набором записей

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

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

НоваяЗапись = Набор.Добавить();
НоваяЗапись.Период       = ТекущаяДата();
НоваяЗапись.Номенклатура = Ном;
НоваяЗапись.ТипЦен       = ТипРозница;
НоваяЗапись.Цена         = 1500;

Набор.Записать(Истина);

Разница с менеджером записи принципиальная:

  • 🔸 Менеджер оперирует одной записью по полному ключу измерений. Если задать не все измерения, запись не будет однозначной.
  • 🔸 Набор оперирует группой записей по частичному отбору. Записать(Истина) заместит все записи, попавшие под отбор, — удобно, чтобы переписать всю историю цен по номенклатуре одним вызовом.

Отбор набора можно ставить по нескольким измерениям сразу — тогда замещается только их пересечение. Это основной способ массовой перезаписи независимого регистра без поэлементного цикла.

Если нужно заместить все записи по номенклатуре — ставьте отбор только по Номенклатура. Если по конкретной паре «номенклатура + тип цен» — добавляйте в отбор оба измерения. Пустой набор с установленным отбором и Записать(Истина) просто удалит записи, попавшие под отбор.

Периодичность: как перезапись зависит от периода

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

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

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

При перезаписи периодического регистра всегда задавайте Период осознанно. Запись с Период = ТекущаяДата() и запись с Период = НачалоМесяца(ТекущаяДата()) при периодичности «в пределах месяца» — это одна и та же запись.

Как перезаписать подчинённый регистратору регистр

У записей такого регистра есть измерение-регистратор (документ). Менеджер записи здесь бесполезен — работают только наборы записей. Типовой приём: взять набор по регистратору, очистить, заполнить заново и записать с замещением.

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

Набор.Очистить();                    // убираем всё, что было по этому регистратору

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

Набор.Записать(Истина);              // Истина — заместить все записи по отбору

Ключевой момент — метод Записать у набора:

  • 📌 Записать(Истина) — платформа удалит все записи, попадающие под текущий отбор, и запишет то, что сейчас в наборе. Это и есть перезапись «начисто».
  • 📌 Записать(Ложь) — записи из набора добавятся к уже существующим, ничего не удаляя.

Отбор обязательно должен совпадать с тем, по которому вы читали. Если открыть пустой набор с отбором по регистратору и записать его с Истина, все записи этого регистратора будут удалены — это штатный способ «очистить движения» документа.

Запись набора с замещением — операция «всё или ничего» в пределах отбора. Перед массовой чисткой движений в рабочей базе убедитесь, что отбор сужен до нужного регистратора, иначе рискуете снести чужие данные.

Массовая перезапись регистра сведений запросом

Когда нужно пересчитать сотни и тысячи записей, поэлементная работа «прочитал-изменил-записал» слишком медленна. Схема другая: выбрать данные запросом, а потом в цикле писать подготовленные значения.

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|   Цены.Номенклатура КАК Номенклатура,
|   Цены.Цена КАК Цена
|ИЗ
|   РегистрСведений.ЦеныНоменклатуры КАК Цены
|ГДЕ
|   Цены.ТипЦен = &СтарыйТип";
Запрос.УстановитьПараметр("СтарыйТип", СтарыйТип);

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

Пока Выборка.Следующий() Цикл
    Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
    Менеджер.Период       = ТекущаяДата();
    Менеджер.Номенклатура = Выборка.Номенклатура;
    Менеджер.ТипЦен       = НовыйТип;
    Менеджер.Цена         = Выборка.Цена * 1.05;
    Менеджер.Записать(Истина);
КонецЦикла;

Запрос здесь нужен, чтобы не читать регистр внутри цикла: вся выборка готовится один раз, а цикл только пишет. Это разница в разы по времени на больших объёмах.

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

Блокировки и обработка ошибок при записи

Любая запись регистра выполняется в транзакции. Если пишете партию записей и хотите, чтобы при сбое откатилась вся партия, оберните её в явную транзакцию с обработкой исключения:

НачатьТранзакцию();
Попытка
    Для Каждого Элемент Из Партия Цикл
        Менеджер = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
        Менеджер.Период       = ТекущаяДата();
        Менеджер.Номенклатура = Элемент.Номенклатура;
        Менеджер.ТипЦен       = Элемент.ТипЦен;
        Менеджер.Цена         = Элемент.Цена;
        Менеджер.Записать(Истина);
    КонецЦикла;
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ЗаписьЖурналаРегистрации("Перезапись цен",
        УровеньЖурналаРегистрации.Ошибка, , ,
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    ВызватьИсключение;
КонецПопытки;

Правила, которые убирают большинство проблем:

  • ✅ После ОтменитьТранзакцию() всегда либо пробрасывайте исключение (ВызватьИсключение), либо корректно завершайте обработку. Проглатывать ошибку молча нельзя.
  • ✅ Текст ошибки берите из ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) — он содержит причину отказа записи.
  • ✅ Не держите открытую транзакцию на время долгих вычислений: чем короче транзакция, тем меньше конфликтов блокировок.

Если в базе включён управляемый режим блокировок, перед чтением-изменением-записью ставьте управляемую блокировку явно — иначе возможны конфликты при параллельной работе пользователей:

Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.ЦеныНоменклатуры");
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Элемент.УстановитьЗначение("Номенклатура", Ном);
Блокировка.Заблокировать();

Частая ошибка записи — «Запись с указанными ключевыми полями уже существует». Она возникает, когда менеджер пишут с параметром Ложь, а запись по ключу уже есть. Решение простое: если задача — именно перезаписать, ставьте Истина.

Как ускорить массовую перезапись

На больших объёмах важно не само количество записей, а число обращений к базе и длина транзакций. Что реально помогает:

  • ⚡ Пишите наборами, а не по одной записи. Для подчинённых регистров один Записать(Истина) на регистратор заменяет десятки отдельных записей — это одна операция вместо многих.
  • ⚡ Дробите на партии. Обрабатывайте по 500–1000 записей в одной транзакции, а не весь массив целиком: короткая транзакция реже упирается в блокировки и не раздувает журнал.
  • ⚡ Готовьте данные запросом заранее. Никакого чтения регистра внутри цикла записи.
  • ⚡ Отключайте лишнюю логику на время загрузки. Свойство ОбменДанными.Загрузка = Истина у набора/менеджера отключает проверки и обработчики, которые на обычной записи не нужны при массовом переносе данных.
  • ⚡ Индексируйте измерение отбора. Если регулярно перезаписываете записи с отбором по измерению, у которого в конфигураторе не включена индексация, чтение набора будет идти по полному перебору. Включите «Индексировать» у этого измерения — отбор станет выборкой по индексу.
Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать(Истина);

ОбменДанными.Загрузка = Истина снимает контроль со стороны кода подписок и проведения. Пользуйтесь им только для технической загрузки заведомо корректных данных, а не как «кнопкой ускорения» в обычных операциях.

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

Отдельно стоит решить, нужна ли перезапись вообще. Если задача — просто добавить записи, которых ещё нет, менеджер с параметром Истина подойдёт и так, без предварительного чтения. А если вы меняете одно ресурсное значение у одной записи, набор записей с полной перезаписью по отбору избыточен — достаточно менеджера. Перезапись набором оправдана там, где нужно заместить группу записей целиком.

В чём разница между менеджером записи и набором записей?
Менеджер записи работает с одной записью независимого регистра по полному ключу измерений. Набор записей работает с группой записей по отбору и обязателен для регистров, подчинённых регистратору.
Что делает параметр `Истина` в методе `Записать`?
Для менеджера — замещает запись с таким же ключом (или добавляет, если её не было). Для набора — удаляет все записи, попавшие под текущий отбор, и пишет на их место содержимое набора.
Почему запись в периодический регистр не сохраняется?
Скорее всего, не заполнен Период. Для периодического регистра период входит в ключ: без него платформа не знает, какую запись замещать.
Как удалить движения документа по регистру сведений?
Открыть набор записей с отбором по этому регистратору, не добавляя строк, и вызвать Записать(Истина). Пустой набор заместит записи регистратора — они удалятся.
Можно ли перезаписать подчинённый регистр менеджером записи?
Нет. У такого регистра менеджер записи не создаётся для изменения — используйте набор записей с отбором по регистратору.