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