Как программно изменить дополнительный реквизит номенклатуры в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке и автоматизации в среде 1С:Предприятие 8.3 одной из частых задач является пакетное или программное заполнение свойств объектов. В современных типовых конфигурациях (таких как 1С:Управление торговлей 11, 1С:ERP Управление предприятием 2, 1С:Комплексная автоматизация 2) свойства объектов делятся на дополнительные реквизиты и дополнительные сведения. Механизм их хранения реализован на базе Библиотеки стандартных подсистем (БСП) и имеет свои архитектурные особенности. Если задачу требуется решить без написания кода, можно использовать платформу для создания программных роботов и автоматизации действий.

Часто начинающие разработчики сталкиваются с тем, что написанный ими код выполняется без видимых ошибок, однако новые значения дополнительных реквизитов в карточке номенклатуры не сохраняются. В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем типичные ошибки, рассмотрим классический вариант исправления кода через работу с табличной частью и изучим наиболее правильный, современный метод записи свойств с использованием штатного API БСП.

Анализ исходной проблемы и разбор типичной ошибки

Давайте проанализируем исходный код, с которым столкнулся автор темы. В его обработчике выполнялся обход выборки номенклатуры, получался объект, выполнялся поиск дополнительного реквизита в табличной части, после чего происходило присвоение нового значения. Однако изменения не записывались в базу данных.

Выясним причину такого поведения системы, взглянув на проблемный участок кода:


Значение = Номенкл.ДополнительныеРеквизиты.Найти(ДопСвойство);
Если Значение = Неопределено Тогда
    Значение = Строка(Номенкл.Артикул) + ".jpg";
КонецЕсли;
Номенкл.Записать();

В чем кроется главная ошибка? Метод Найти() для табличной части возвращает объект типа СтрокаТабличнойЧасти, если строка найдена, и значение Неопределено, если строка с указанным свойством отсутствует.

Когда условие Значение = Неопределено выполняется, разработчик пытается напрямую присвоить переменной Значение строковый тип: Значение = Строка(Номенкл.Артикул) + ".jpg". В этот момент локальная переменная Значение просто перезаписывается в памяти новым строковым значением. При этом сама табличная часть справочника никак не изменяется: в нее не добавляется новая строка, и в базу данных записывается абсолютно неизмененный объект номенклатуры.

Даже если бы строка была найдена, переменная Значение содержала бы всю строку табличной части целиком (объект СтрокаТабличнойЧасти), а не конкретное поле. Попытка перезаписать саму строку ТЧ простым присвоением строкового значения также привела бы к ошибке типов или отсутствию результата.

Решение №1: Прямое редактирование табличной части (Классический метод)

Для того чтобы успешно изменить или добавить дополнительный реквизит напрямую через объект, нам необходимо четко разделять понятия строки табличной части и непосредственно значения свойства в этой строке. Табличная часть ДополнительныеРеквизиты содержит две ключевые колонки: Свойство (ссылка на План видов характеристик ДополнительныеРеквизитыИСведения) и Значение (хранит само значение свойства).

Разберем по шагам правильный алгоритм прямого изменения табличной части:

  1. Получаем объект номенклатуры.
  2. Находим нужное свойство в Плане видов характеристик по наименованию (или по уникальному идентификатору).
  3. Используем метод Найти() табличной части ДополнительныеРеквизиты для поиска строки по колонке Свойство.
  4. Если строка найдена, мы обращаемся к ее свойству Значение и перезаписываем его.
  5. Если строка не найдена, мы принудительно добавляем новую строку в табличную часть с помощью метода Добавить(), после чего заполняем колонки Свойство и Значение.
  6. Записываем объект номенклатуры.

Посмотрим на пример практической реализации данного алгоритма:


&НаСервере
Процедура ОбновитьДопРеквизитНапрямую()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.Артикул КАК Артикул
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   НЕ Номенклатура.ЭтоГруппа";
        
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // Получаем свойство ОДИН РАЗ до начала цикла для экономии ресурсов СУБД
    ДопСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Код товара");
    
    Если ДопСвойство.Пустая() Тогда
        ВызватьИсключение "Не найден дополнительный реквизит ""Код товара""!";
    КонецЕсли;
    
    Пока Выборка.Следующий() Цикл
        
        НоменклОбъект = Выборка.Ссылка.ПолучитьОбъект();
        
        // Пытаемся найти уже существующую строку с этим свойством в ТЧ номенклатуры
        СтрокаТЧ = НоменклОбъект.ДополнительныеРеквизиты.Найти(ДопСвойство, "Свойство");
        
        НовоеЗначение = Строка(Выборка.Артикул) + ".jpg";
        
        Если СтрокаТЧ <> Неопределено Тогда
            // Если строка найдена, проверяем, изменилось ли значение, чтобы избежать лишней записи
            Если СтрокаТЧ.Значение <> НовоеЗначение Тогда
                СтрокаТЧ.Значение = НовоеЗначение;
                НоменклОбъект.Записать();
            КонецЕсли;
        Иначе
            // Если строки со свойством нет — добавляем новую строку
            СтрокаТЧ = НоменклОбъект.ДополнительныеРеквизиты.Добавить();
            СтрокаТЧ.Свойство = ДопСвойство;
            СтрокаТЧ.Значение = НовоеЗначение;
            НоменклОбъект.Записать();
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Оптимизация производительности: устраняем критические антипаттерны

В коде, который часто пишут новички, скрыты две серьезные проблемы производительности, способные заблокировать работу пользователей в реальной базе данных при обработке большого каталога товаров:

Решение №2: Использование программного интерфейса БСП (Рекомендуемый метод)

Прямая модификация табличных частей объектов через метод ПолучитьОбъект() и последующую перезапись крайне нежелательна в современных ERP-системах. БСП предоставляет разработчикам высокоуровневый, стандартизированный и безопасный инструмент для управления свойствами — общий модуль УправлениеСвойствами. Для быстрого изучения программного API и поиска готовых примеров вызовов можно использовать справочник по методам БСП.

Использование БСП API обладает рядом неоспоримых преимуществ:

Рассмотрим, как правильно выполнить запись дополнительного реквизита с использованием методов БСП:


&НаСервере
Процедура ЗаписатьДопРеквизитЧерезБСП(СсылкаНаНоменклатуру, СвойствоКодТовара, НовоеЗначение)
    
    // Создаем таблицу значений для передачи измененных свойств в БСП
    ТаблицаСвойств = Новый ТаблицаЗначений;
    ТаблицаСвойств.Колонки.Добавить("Свойство", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения"));
    ТаблицаСвойств.Колонки.Добавить("Значение");
    
    // Заполняем строку новыми данными
    НоваяСтрока = ТаблицаСвойств.Добавить();
    НоваяСтрока.Свойство = СвойствоКодТовара;
    НоваяСтрока.Значение = НовоеЗначение;
    
    // Вызываем стандартный метод БСП для записи свойств у объекта
    УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаНоменклатуру, ТаблицаСвойств);
    
КонецПроцедуры

В данном сценарии метод УправлениеСвойствами.ЗаписатьСвойстваУОбъекта принимает ссылку на целевой объект (номенклатуру) и таблицу с измененными свойствами. Система самостоятельно выполнит все необходимые проверки, обновит табличную часть и запишет объект наиболее оптимальным для СУБД способом.

Важное различие: Дополнительные реквизиты vs Дополнительные сведения

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

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

Дополнительные сведения — это внешние данные по отношению к объекту. Они хранятся отдельно от номенклатуры в независимом регистре сведений РегистрСведений.ДополнительныеСведения. Изменение сведений не изменяет сам объект-владелец. Это позволяет добавлять или менять информацию по объектам даже в тех периодах, которые закрыты для редактирования пользователям.

Если перед вами стоит задача программно записать не реквизит, а Дополнительное сведение, код прямого обращения к табличной части работать не будет. В этом случае необходимо работать с набором записей регистра сведений. Рассмотрим пример записи дополнительного сведения:


&НаСервере
Процедура ЗаписатьДополнительноеСведение(ОбъектСсылка, СвойствоСведение, ЗначениеСведения)
    
    НаборЗаписей = РегистрыСведений.ДополнительныеСведения.СоздатьНаборЗаписей();
    
    // Устанавливаем жесткий отбор по объекту-владельцу и свойству
    НаборЗаписей.Отбор.Объект.Установить(ОбъектСсылка);
    НаборЗаписей.Отбор.Свойство.Установить(СвойствоСведение);
    
    // Добавляем запись в набор
    НоваяСтрока = НаборЗаписей.Добавить();
    НоваяСтрока.Объект = ОбъектСсылка;
    НоваяСтрока.Свойство = СвойствоСведение;
    НоваяСтрока.Значение = ЗначениеСведения;
    
    Попытка
        НаборЗаписей.Записать(Истина); // Параметр Истина замещает существующие записи по отбору
    Исключение
        ЗаписьЖурналаРегистрации("Программное изменение свойств",
            УровеньЖурналаРегистрации.Ошибка,,, 
            "Ошибка записи сведений для объекта: " + ОбъектСсылка + ". Описание: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Тонкости работы с типами данных дополнительных реквизитов

В практических задачах дополнительным реквизитам не всегда присваиваются простые типы данных вроде "Строка", "Число" или "Булево". Очень часто тип значения дополнительного реквизита настроен как "Дополнительное значение". В этом случае типом данных реквизита является ссылка на элемент подчиненного справочника Справочники.ЗначенияСвойствОбъектов.

Если вы попытаетесь записать обычную строку в реквизит с типом "Дополнительное значение", система либо очистит поле, либо выдаст ошибку несоответствия типов при интерактивном открытии карточки номенклатуры.

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


&НаСервере
Функция ПолучитьСсылкуНаДополнительноеЗначение(ДопСвойство, ТекстовоеЗначение)
    
    // Ищем значение среди элементов, подчиненных нашему свойству-владельцу
    СсылкаНаЗначение = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию(ТекстовоеЗначение, Истина,, ДопСвойство);
    
    // Если значения с таким наименованием еще нет, создаем его программно
    Если СсылкаНаЗначение.Пустая() Тогда
        
        НовоеЗначениеОбъект = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
        НовоеЗначениеОбъект.Владелец = ДопСвойство;
        НовоеЗначениеОбъект.Наименование = ТекстовоеЗначение;
        
        Попытка
            НовоеЗначениеОбъект.Записать();
            СсылкаНаЗначение = НовоеЗначениеОбъект.Ссылка;
        Исключение
            ВызватьИсключение "Не удалось записать новое значение свойства: " + ОписаниеОшибки();
        КонецПопытки;
        
    КонецЕсли;
    
    Возврат СсылкаНаЗначение;
    
КонецФункции

Используя данную функцию, вы сначала гарантированно получаете корректную ссылку на элемент справочника ЗначенияСвойствОбъектов, после чего записываете эту ссылку в колонку Значение табличной части ДополнительныеРеквизиты номенклатуры или передаете ее в параметры методов общего модуля БСП.

Резюмируя, при программной работе с дополнительными реквизитами в 1С всегда отдавайте предпочтение методам БСП. Если же вы используете прямое изменение табличной части объекта, обязательно оптимизируйте циклы поиска метаданных и контролируйте фактическое изменение данных перед выполнением метода записи.

← На главную