В процессе разработки и кастомизации конфигураций на платформе 1С:Предприятие 8.3, особенно таких крупных решений, как «1С:Управление торговлей 11.4», «1С:ERP» или «1С:Комплексная автоматизация», разработчики часто сталкиваются с необходимостью массового изменения свойств объектов. Одним из наиболее востребованных объектов для таких манипуляций является справочник Номенклатура.
Однако механизмы хранения свойств в современных конфигурациях, построенных на базе Библиотеки стандартных подсистем (БСП), существенно отличаются от простой структуры старых версий систем. Попытки изменить эти данные «напрямую» без понимания внутренней архитектуры хранения часто приводят к критическим ошибкам — вплоть до повреждения глобальных метаданных и справочников в базе данных. В этой статье мы подробно разберем типичную ошибку разработчика, проанализируем архитектуру хранения свойств БСП и рассмотрим три надежных способа решения этой задачи: два программных и один интерактивный (типовой).
Давайте проанализируем код, который часто пытаются использовать начинающие программисты 1С для массового изменения свойств номенклатуры:
Для Каждого СтрокаТЗ из Объект.Стикеры Цикл
Если СтрокаТЗ.Флажок = Истина Тогда
Строка = СтрокаТЗ.Стикер.Ссылка.Владелец.ПолучитьОбъект();
Строка.Наименование = Объект.Выбор1.Наименование;
Строка.Записать();
Иначе
Отказ = Истина;
КонецЕсли;
КонецЦикла;
Выясним причину, почему данный код работает некорректно и изменяет имя самого свойства вместо его значения у конкретного товара. В строке Строка = СтрокаТЗ.Стикер.Ссылка.Владелец.ПолучитьОбъект(); автор кода обращается к реквизиту Владелец элемента справочника ЗначенияСвойствОбъектов.
В архитектуре БСП справочник ЗначенияСвойствОбъектов хранит в себе конкретные значения дополнительных реквизитов (например, для свойства «Стикер» значениями могут быть «Новинка», «Акция», «Хит продаж»). Владельцем этих значений является элемент плана видов характеристик (ПВХ) ДополнительныеРеквизитыИСведения, который описывает само свойство («Стикер»). Получая объект владельца и перезаписывая его наименование через Строка.Наименование = Объект.Выбор1.Наименование;, разработчик переименовывает само свойство в системе. В результате свойство «Стикер» во всей базе данных переименовывается, например, в «Хит продаж», что ломает логику учета и отображения реквизитов у всех товаров сразу.
Прежде чем перейти к написанию корректного кода, разберем по шагам, где и как на самом деле хранятся дополнительные свойства объектов в БСП. Они разделены на две принципиальные категории:
ДополнительныеРеквизиты. Каждая строка этой табличной части содержит два ключевых реквизита: Свойство (ссылка на ПВХ ДополнительныеРеквизитыИСведения) и Значение (конкретное значение свойства).ДополнительныеСведения. Это позволяет добавлять информацию к объектам без необходимости открывать их на чтение и запись (что важно, например, при закрытых периодах).Ниже мы рассмотрим правильные методы программного изменения этих данных.
Для безопасного и корректного изменения свойств лучше всего использовать встроенный общий модуль БСП УправлениеСвойствами. Метод ЗаписатьСвойстваУОбъекта берет на себя всю рутину по проверке типов, блокировке объектов, определению места хранения свойства (в табличной части или в регистре сведений) и записи данных в транзакции.
Рассмотрим подробнее пример кода, который решает задачу автора темы правильным и безопасным способом:
// Шаг 1. Создаем и подготавливаем таблицу значений для новых свойств
ТаблицаСвойств = Новый ТаблицаЗначений;
ТаблицаСвойств.Колонки.Добавить("Свойство");
ТаблицаСвойств.Колонки.Добавить("Значение");
// Шаг 2. Находим целевое свойство в Плане видов характеристик
СвойствоСтикер = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Стикер");
Если ЗначениеЗаполнено(СвойствоСтикер) Тогда
// Заполняем таблицу новыми данными.
// В качестве значения передаем ссылку на справочник "Значения свойств объектов"
НоваяСтрока = ТаблицаСвойств.Добавить();
НоваяСтрока.Свойство = СвойствоСтикер;
НоваяСтрока.Значение = Объект.Выбор1; // Ссылка на выбранное на форме значение
// Шаг 3. Обходим в цикле коллекцию номенклатуры и записываем свойства
Для Каждого СтрокаТЗ Из Объект.Стикеры Цикл
Если СтрокаТЗ.Флажок Тогда
Попытка
// Используем стандартный метод БСП для безопасной записи свойства
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СтрокаТЗ.Номенклатура, ТаблицаСвойств);
Исключение
// Обрабатываем возможные ошибки блокировки или прав доступа
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
НСтр("ru = 'Не удалось записать свойство для объекта: '") + СтрокаТЗ.Номенклатура + ". " + ОписаниеОшибки()
);
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Данный подход гарантирует, что система автоматически разберется, куда записывать данные — в табличную часть номенклатуры или в регистр сведений ДополнительныеСведения, а также корректно обновит кэш свойств в сеансах пользователей.
Если вы твердо уверены, что ваше свойство настроено именно как дополнительный реквизит (то есть оно хранится внутри объекта номенклатуры), можно прибегнуть к прямому изменению табличной части объекта. Этот способ работает быстрее при пакетной обработке больших массивов данных, однако требует явного получения объекта для записи.
Посмотрим на пример реализации данного алгоритма:
СвойствоСтикер = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Стикер");
Если ЗначениеЗаполнено(СвойствоСтикер) Тогда
Для Каждого СтрокаТЗ Из Объект.Стикеры Цикл
Если СтрокаТЗ.Флажок Тогда
// Получаем объект номенклатуры для редактирования
НоменклатураОбъект = СтрокаТЗ.Номенклатура.ПолучитьОбъект();
Если НоменклатураОбъект = Неопределено Тогда
Продолжить; // Если объект заблокирован или удален
КонецЕсли;
// Ищем строку с нашим свойством в табличной части "ДополнительныеРеквизиты"
СтрокаТЧ = НоменклатураОбъект.ДополнительныеРеквизиты.Найти(СвойствоСтикер, "Свойство");
Если СтрокаТЧ = Неопределено Тогда
// Если свойство ранее не заполнялось у этого товара, добавляем новую строку
СтрокаТЧ = НоменклатураОбъект.ДополнительныеРеквизиты.Добавить();
СтрокаТЧ.Свойство = СвойствоСтикер;
КонецЕсли;
// Устанавливаем новое значение реквизита
СтрокаТЧ.Значение = Объект.Выбор1;
// Записываем измененный объект номенклатуры
Попытка
НоменклатураОбъект.Записать();
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
НСтр("ru = 'Ошибка записи объекта: '") + НоменклатураОбъект.Ссылка + ". " + ОписаниеОшибки()
);
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Важное примечание: Прямой метод требует монопольного доступа к объекту в момент записи. Если объект заблокирован другим пользователем, метод Записать() вызовет исключение, поэтому использование конструкции Попытка ... Исключение здесь строго обязательно.
В конфигурации «1С:Управление торговлей 11.4» заложена мощная функциональность для группового изменения любых реквизитов и свойств, которая позволяет решить задачу автора темы за пару минут без привлечения программиста и написания обработок.
Проанализируем пошагово, как выполнить эту операцию интерактивно:
Система в фоновом режиме корректно и безопасно перезапишет свойства у всех выбранных позиций номенклатуры, соблюдая все правила бизнес-логики и контроля транзакций БСП.