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