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

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

При работе с товарным каталогом в 1С часто возникает необходимость массово обработать данные и заполнить определенные свойства или дополнительные реквизиты номенклатуры. Типичный пример такой задачи — подготовка базы к интеграции с интернет-магазином, когда для каждой позиции каталога требуется заполнить специальное свойство (например, «КодДляСайта») данными из стандартного реквизита Код или Артикул.

В этой статье мы подробно разберем, как решить эту задачу программным путем. Мы изучим механизмы работы со свойствами как для классических конфигураций на обычных формах (Управление торговлей 10.3, Управление производственным предприятием 1.3, Комплексная автоматизация 1.1), так и для современных решений на управляемых формах (Управление торговлей 11, ERP Управление предприятием 2, Комплексная автоматизация 2, Бухгалтерия предприятия 3.0).

Часть 1. Решение для конфигураций на обычных формах (УТ 10.3, УПП 1.3)

В старых конфигурациях архитектура свойств и категорий построена на использовании плана видов характеристик СвойстваОбъектов и независимого непериодического регистра сведений ЗначенияСвойствОбъектов. Данные в этом регистре хранятся в разрезе двух основных измерений: Объект (ссылка на номенклатуру) и Свойство (ссылка на ПВХ).

Рассмотрим ситуацию, с которой столкнулся автор темы: необходимо взять код номенклатуры и записать его в свойство «КодДляСайта» (тип значения — Строка) для каждого элемента справочника. Запрос для выборки данных у нас уже есть, теперь разберем, как правильно организовать обход выборки и запись данных в базу.

Для записи данных в независимый регистр сведений эффективнее и проще всего использовать Менеджер записи. Посмотрим на оптимизированный алгоритм решения этой задачи:


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

Если НЕ ЗначениеЗаполнено(СвойствоКодДляСайта) Тогда
    ВызватьИсключение "Не найдено свойство ""КодДляСайта"" в плане видов характеристик!";
КонецЕсли;

// Шаг 2. Подготавливаем и выполняем запрос для получения данных
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Код КАК Код,
    |	ЗначенияСвойствОбъектов.Значение КАК ТекущееЗначение
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
    |		ПО Номенклатура.Ссылка = ЗначенияСвойствОбъектов.Объект
    |		И (ЗначенияСвойствОбъектов.Свойство = &Свойство)
    |ГДЕ
    |	Номенклатура.ЭтоГруппа = ЛОЖЬ"; // обрабатываем только элементы, исключая группы

Запрос.УстановитьПараметр("Свойство", СвойствоКодДляСайта);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

// Шаг 3. Запускаем транзакцию для ускорения записи в базу данных
НачатьТранзакцию();

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

Важные нюансы оптимизации при работе с большими базами данных

Если в вашем каталоге номенклатуры содержится более 10 000 позиций, выполнение всей операции в рамках одной транзакции может привести к серьезным проблемам: блокировке таблиц базы данных для других пользователей, чрезмерному росту файла транзакций СУБД или даже аварийному завершению сеанса из-за нехватки памяти.

Для предотвращения подобных ситуаций рекомендуется производить запись пакетами (порциями). Мы зафиксируем транзакцию и откроем новую после обработки определенного количества записей (например, каждые 1000 элементов). Посмотрим, как модифицировать цикл обхода:


Счетчик = 0;
РазмерПорции = 1000;

НачатьТранзакцию();
Попытка
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        
        Если ВыборкаДетальныеЗаписи.ТекущееЗначение = ВыборкаДетальныеЗаписи.Код Тогда
            Продолжить;
        КонецЕсли;
        
        МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Объект   = ВыборкаДетальныеЗаписи.Ссылка;
        МенеджерЗаписи.Свойство = СвойствоКодДляСайта;
        МенеджерЗаписи.Значение = ВыборкаДетальныеЗаписи.Код;
        МенеджерЗаписи.Записать(Истина);
        
        Счетчик = Счетчик + 1;
        
        // Фиксируем порцию и сразу открываем новую транзакцию
        Если Счетчик % РазмерПорции = 0 Тогда
            ЗафиксироватьТранзакцию();
            НачатьТранзакцию();
        КонецЕсли;
        
    КонецЦикла;
    
    // Не забываем зафиксировать финальный остаток записей
    ЗафиксироватьТранзакцию();
    Сообщить("Пакетная обработка завершена. Всего обновлено записей: " + Счетчик);
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение ОписаниеОшибки();
КонецПопытки;

Обратите внимание на типы данных! Автор указал, что свойство «КодДляСайта» имеет строковый тип. Это облегчает задачу. Если бы тип значения свойства был ссылочным (например, ссылался на подчиненный справочник ЗначенияСвойствОбъектов), то простая запись строки вызвала бы ошибку несоответствия типов. В таком случае вам сначала потребовалось бы программно создать элемент в подчиненном справочнике значений, а затем записывать в регистр полученную ссылку.

---

Часть 2. Решение для современных конфигураций (УТ 11, ERP 2, КА 2, БП 3.0)

В современных конфигурациях 1С, построенных на управляемых формах, механизм работы со свойствами полностью изменился. За это отвечает подсистема «Свойства» из состава Библиотеки стандартных подсистем (БСП). Здесь свойства разделены на две категории:

  1. Дополнительные реквизиты — хранятся непосредственно «внутри» объектов конфигурации в табличной части ДополнительныеРеквизиты. Они выводятся на форму объекта вместе с основными реквизитами.
  2. Дополнительные сведения — хранятся в независимом регистре сведений ДополнительныеСведения. Они предназначены для хранения информации, которая не должна редактироваться непосредственно при открытии карточки объекта.

Рассмотрим оба варианта программного заполнения данных в современных реалиях.

Вариант А. Заполнение дополнительного реквизита (через табличную часть объекта)

Для изменения дополнительных реквизитов нам необходимо получить объект номенклатуры, найти нужную строку в его табличной части ДополнительныеРеквизиты (или добавить новую, если ее там еще нет), заполнить значения и записать объект.

Проанализируем пример кода:


// Шаг 1. Находим дополнительный реквизит в ПВХ БСП
Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("КодДляСайта (Номенклатура)");

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

Вариант Б. Использование стандартных методов БСП (наиболее надежный способ)

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

Для массовой записи дополнительных реквизитов или сведений оптимально подходит метод общего модуля УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(). Выясним, как применить этот метод на практике:


// Шаг 1. Получаем ссылку на свойство
СвойствоКодДляСайта = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("КодДляСайта (Номенклатура)");

Если ЗначениеЗаполнено(СвойствоКодДляСайта) Тогда
    
    Пока Выборка.Следующий() Цикл
        
        // Шаг 2. Формируем специальную таблицу значений для передачи в БСП
        ТаблицаСвойств = Новый ТаблицаЗначений;
        ТаблицаСвойств.Колонки.Добавить("Свойство", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения"));
        ТаблицаСвойств.Колонки.Добавить("Значение");
        
        // Наполняем таблицу данными для конкретного объекта
        НоваяСтрока = ТаблицаСвойств.Добавить();
        НоваяСтрока.Свойство = СвойствоКодДляСайта;
        НоваяСтрока.Значение = Выборка.Код;
        
        // Шаг 3. Записываем свойства через стандартный метод БСП
        // Метод работает как для Дополнительных реквизитов, так и для Дополнительных сведений автоматически!
        УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(Выборка.Ссылка, ТаблицаСвойств);
        
    КонецЦикла;
    
    Сообщить("Все свойства успешно обработаны через БСП!");
КонецЕсли;

Этот способ обладает огромным преимуществом: вам не нужно задумываться о том, где именно хранится свойство (в ТЧ объекта или в регистре сведений) — программный интерфейс БСП самостоятельно определит структуру хранения и корректно запишет данные, выполнив при этом все установленные в системе подписки на события.

Резюме и правила хорошего тона при написании обработок

Подводя итоги, сформулируем несколько важных правил, которые уберегут вас от ошибок при программном заполнении свойств объектов в 1С:

← На главную