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

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

При разработке обработок загрузки номенклатуры из внешних источников (например, из файлов Excel) часто возникает задача записи дополнительных реквизитов и характеристик товара. Нередко разработчики сталкиваются с ситуацией, когда для вновь создаваемых позиций свойства записываются успешно, а у уже существующих элементов в базе данные не обновляются либо перезаписываются с ошибками. Проанализируем причины такого поведения и разберем корректные способы решения этой задачи в конфигурациях на обычных формах, таких как «1С:Управление торговлей 10.3».

Анализ типовых ошибок при записи свойств

Рассмотрим распространенные ошибки, которые приводят к сбоям при попытке обновить свойства номенклатуры:

  1. Неправильная работа с набором записей. Если использовать СоздатьНаборЗаписей(), установить отбор по объекту и просто добавить новую строку без предварительного чтения существующих данных, при вызове метода Записать(Истина) все прежние свойства номенклатуры будут безвозвратно удалены, а запишется только новое. Если же передать параметр Ложь (или не учесть режим замещения), при наличии уже существующей записи возникнет конфликт ключевых полей.
  2. Пустой блок перехвата исключений. Конструкция Попытка ... Исключение КонецПопытки без вывода текста ошибки (ОписаниеОшибки()) маскирует проблему, создавая ложное впечатление, что код отработал штатно, хотя запись на самом деле была отклонена СУБД.
  3. Несоответствие типов данных. В плане видов характеристик СвойстваОбъектов для каждого реквизита жестко задан тип значения. Если для свойства определен тип «Строка» или «Число», а в регистр передается ссылка на справочник ЗначенияСвойствОбъектов (или наоборот), платформа вызовет исключение при несоответствии типов.
  4. Поиск значений без указания владельца. Справочник ЗначенияСвойствОбъектов является подчиненным плану видов характеристик СвойстваОбъектов. Вызов метода НайтиПоНаименованию без указания владельца свойства может вернуть элемент с совпадающим названием, принадлежащий совершенно другому свойству.

Способ 1. Использование менеджера записи (рекомендуемый вариант)

Регистр сведений ЗначенияСвойствОбъектов является непериодическим и независимым. Его ключевыми измерениями выступают Объект (ссылка на номенклатуру) и Свойство (ссылка на элемент плана видов характеристик). Для точечной перезаписи конкретного свойства удобнее всего использовать СоздатьМенеджерЗаписи(). В этом случае не требуется вычитывать весь список свойств позиции номенклатуры.

Разберем по шагам реализацию через менеджер записи:


Функция ЗаписатьСвойствоТовара(СсылкаНоменклатура, СсылкаСвойство, ЗначениеДляЗаписи)
    
    Если Не ЗначениеЗаполнено(СсылкаНоменклатура) Или Не ЗначениеЗаполнено(СсылкаСвойство) Тогда
        Возврат Ложь;
    КонецЕсли;

    МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Объект = СсылкаНоменклатура;
    МенеджерЗаписи.Свойство = СсылкаСвойство;
    МенеджерЗаписи.Значение = ЗначениеДляЗаписи;

    Попытка
        // Параметр Истина гарантирует замещение записи при ее наличии
        МенеджерЗаписи.Записать(Истина);
        Возврат Истина;
    Исключение
        Сообщить("Ошибка при записи свойства: " + ОписаниеОшибки());
        Возврат Ложь;
    КонецПопытки;

КонецФункции

Посмотрим на практический пример использования данной функции при обработке строковых свойств (например, кода производителя или артикула):


// Оптимизация: находим свойство заранее до входа в цикл обработки строк
ПланСвойств = ПланыВидовХарактеристик.СвойстваОбъектов;
СвойствоКод = ПланСвойств.НайтиПоНаименованию("Код производителя, MPN");

Если ЗначениеЗаполнено(СвойствоКод) Тогда
    ЗначениеКода = СокрЛП(ТекущаяСтрокаТаблицы.ПартНомер);
    Если ЗначениеЗаполнено(ЗначениеКода) Тогда
        ЗаписатьСвойствоТовара(ТекНоменклатура, СвойствоКод, ЗначениеКода);
    КонецЕсли;
Иначе
    Сообщить("Свойство 'Код производителя, MPN' не найдено в базе данных!");
КонецЕсли;

Способ 2. Обновление через набор записей с предварительным чтением

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

Рассмотрим подробнее программный код такого подхода:


Процедура ОбновитьСвойствоЧерезНабор(ТекНом, СсылкаСвойство, НовоеЗначение)

    НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Объект.Установить(ТекНом);
    
    // Считываем текущие свойства данного объекта из базы
    НаборЗаписей.Прочитать();

    СтрокаСвойства = Неопределено;
    Для Каждого Запись Из НаборЗаписей Цикл
        Если Запись.Свойство = СсылкаСвойство Тогда
            СтрокаСвойства = Запись;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    // Если свойство еще не было задано, добавляем новую запись в набор
    Если СтрокаСвойства = Неопределено Тогда
        СтрокаСвойства = НаборЗаписей.Добавить();
        СтрокаСвойства.Объект = ТекНом;
        СтрокаСвойства.Свойство = СсылкаСвойство;
    КонецЕсли;

    СтрокаСвойства.Значение = НовоеЗначение;

    Попытка
        // Записываем обновленный набор с замещением
        НаборЗаписей.Записать(Истина);
    Исключение
        Сообщить("Не удалось обновить свойства номенклатуры: " + ОписаниеОшибки());
    КонецПопытки;

КонецПроцедуры

Способ 3. Работа со справочными свойствами (ЗначенияСвойствОбъектов)

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

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


Функция ПолучитьСсылкуНаЗначениеСвойства(СсылкаСвойство, ТекстЗначения)

    Если Не ЗначениеЗаполнено(ТекстЗначения) Тогда
        Возврат Справочники.ЗначенияСвойствОбъектов.ПустаяСсылка();
    КонецЕсли;

    // Выполняем поиск строго в рамках нужного свойства-владельца
    НайденноеЗначение = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию(
        ТекстЗначения, 
        Истина, 
        , 
        СсылкаСвойство
    );

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

    Возврат НайденноеЗначение;

КонецФункции

После получения ссылки на элемент подчиненного справочника мы передаем ее в качестве значения в менеджер записи или набор записей, как было показано в предыдущих примерах.

Рекомендации по оптимизации загрузки данных

Для обеспечения стабильной и быстрой работы обработки придерживайтесь следующих правил:

  1. Выносите поиск свойств из циклов. Никогда не вызывайте НайтиПоНаименованию для планов видов характеристик внутри цикла по строкам загружаемого документа. Выполните поиск нужных свойств один раз до начала обхода строк и сохраните ссылки в переменные.
  2. Контролируйте типы данных. Если свойство числовое (например, вес, длина, ширина), преобразуйте строковые данные из файла с помощью функции Число(), предварительно очистив строку от пробелов и заменив разделители дроби.
  3. Разделяйте логику. Не привязывайте процедуру записи дополнительных реквизитов исключительно к ветке создания новой номенклатуры. Процедура установки свойств должна принимать ссылку на элемент справочника Номенклатура и выполняться независимо от того, был ли товар только что создан или найден в базе как существующий.
← На главную