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