При программном создании характеристик номенклатуры в конфигурации 1С:Розница (редакция 2.3) часто возникают сложности с заполнением их дополнительных реквизитов. Особенно эта проблема актуальна, когда дополнительные реквизиты имеют тип значения «Дополнительное значение», а не простую строку. Рассмотрим подробнее, как корректно создавать характеристики и заполнять все необходимые сведения.
Первоначально автор задачи столкнулся с тем, что при создании характеристик номенклатуры программно, такие базовые поля, как наименование и владелец, заполнялись успешно. Однако дополнительные реквизиты, которые должны были нести информацию о размере и цвете, оставались незаполненными, хотя в переменных на момент записи значения присутствовали.
Такая ситуация типична для начинающих разработчиков, поскольку работа с дополнительными реквизитами в 1С имеет свою специфику, особенно при использовании типов значений, отличных от примитивных (строка, число, дата).
Прежде чем мы перейдем к непосредственному решению, давайте разберем ключевые объекты и концепции, лежащие в основе работы с дополнительными реквизитами в системе 1С. Понимание этой архитектуры критически важно для успешной программной работы.
ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения: Этот объект является "определением" самого дополнительного реквизита. Например, если нам нужен дополнительный реквизит "Цвет" или "Размер", то именно здесь хранятся его метаданные: заголовок, тип значения, принадлежность к набору свойств и так далее. При программном поиске или создании нового дополнительного реквизита мы будем обращаться именно к этому плану видов характеристик.Справочник.ЗначенияСвойствОбъектов (или Справочник.ЗначенияСвойствОбъектовИерархия): Этот справочник играет ключевую роль, когда тип значения дополнительного реквизита установлен как "Дополнительное значение". В этом случае система ожидает не произвольную строку, а ссылку на предопределенное или пользовательское значение из этого справочника. Например, для реквизита "Цвет" здесь могут быть предопределены значения "Красный", "Синий", "Зеленый". При программном заполнении такого реквизита мы должны найти или создать соответствующий элемент этого справочника и передать его ссылку.Справочник.НаборыДополнительныхРеквизитовИСведений: Этот справочник определяет, для каких объектов системы (например, для "ХарактеристикиНоменклатуры", "Номенклатура", "Контрагенты" и т.д.) будут использоваться определенные наборы дополнительных реквизитов. Внутри каждого элемента этого справочника есть табличная часть, которая содержит ссылки на элементы из ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения, тем самым связывая конкретные дополнительные реквизиты с конкретным видом объекта.Понимание того, что дополнительный реквизит типа "Дополнительное значение" ожидает ссылку на элемент справочника ЗначенияСвойствОбъектов, является краеугольным камнем в решении обсуждаемой проблемы. Передача простой строки в этом случае не сработает, так как система будет ожидать объект, а не примитивный тип данных.
Рассмотрим первоначальный код, представленный автором, чтобы понять, где именно возникли трудности:
//Получаем список доп.реквизитов с формы
ТипСправочник = Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения") ;
ТипСтрока = Новый ОписаниеТипов("Строка");
ТЗНаФорме = Объект.ДополнительныеРеквизиты;
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Свойство",ТипСправочник,"Свойство" );
ТЗ.Колонки.Добавить("Значение",ТипСтрока,"Значение"); // <-- Здесь потенциальная проблема
Для каждого СтрокаТЗНаФорме Из ТЗНаФорме Цикл
ЗаполнитьЗначенияСвойств(ТЗ.Добавить,СтрокаТЗНаФорме);
КонецЦикла;
// ... пропускаем код поиска цвета и проверки ...
//Обходим список свойства Размер и на каждый формируем характеристику
Для Каждого Стр Из ТЗ Цикл
Если Стр.Свойство = СтрЦвет.Свойство Тогда
Прервать;
Иначе
Наименование = (Стр.Значение + ", " + СтрЦвет.Значение);
// ... проверка существования характеристики ...
Если Запрос.Выполнить.Выбрать.Количество<=0 Тогда
//Если нет, то задаем характеристику номенклатуре
Характеристика = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент;
Характеристика.Владелец = ВладелецСсылка;
Характеристика.Наименование = Наименование;
НоваяСтрока = Характеристика.ДополнительныеРеквизиты.Добавить; // Добавили первую строку
НоваяСтрока.Свойство = Стр.Свойство;
НоваяСтрока.Значение = Стр.Значение; // Записали Размер
// !!!!! Здесь предполагается добавление второй строки, но `Добавить` отсутствует
НоваяСтрока.Свойство = СтрЦвет.Свойство; // <-- Здесь перезаписывается свойство первой строки
НоваяСтрока.Значение = СтрЦвет.Значение; // <-- Здесь перезаписывается значение первой строки
Попытка
Характеристика.Записать;
Исключение
ОбщегоНазначения.СообщитьПользователю("Ошибка при записи новой характеристики");
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Мы выявили две основные проблемы в приведенном коде, которые мешали корректному заполнению дополнительных реквизитов:
ДополнительныеРеквизиты: В коде, где происходит заполнение табличной части характеристик, присутствует логическая ошибка. Строка НоваяСтрока = Характеристика.ДополнительныеРеквизиты.Добавить; вызывается только один раз. После этого мы присваиваем ей значения для "Размера", а затем, не добавляя новую строку, пытаемся присвоить те же полям НоваяСтрока.Свойство и НоваяСтрока.Значение данные для "Цвета". Это приводит к тому, что значения "Размера" просто перезаписываются значениями "Цвета", и в характеристике остается только один дополнительный реквизит – "Цвет".ДополнительныеРеквизиты нельзя передавать простую строку. Система ожидает ссылку на элемент справочника Справочник.ЗначенияСвойствОбъектов. В коде автора колонка ТЗ "Значение" была описана как "Строка", что приводило к ошибке типа при попытке записи.Теперь, когда мы понимаем причины возникновения ошибок, давайте рассмотрим шаги для их устранения и корректного программного заполнения дополнительных реквизитов.
Справочник.НаборыДополнительныхРеквизитовИСведений.ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения, которые соответствуют нашим "Цвету" и "Размеру".Справочник.ЗначенияСвойствОбъектов. Если значения нет, возможно, придется его создать.ДополнительныеРеквизиты характеристики: Для каждого дополнительного реквизита, который мы хотим установить, необходимо вызвать метод Добавить табличной части Характеристика.ДополнительныеРеквизиты, чтобы создать отдельную строку для каждого свойства.Рассмотрим исправленный и дополненный пример кода, который позволит нам решить поставленную задачу. Мы будем использовать те же входные данные, что и автор, но исправим логику добавления строк и работу с типами значений.
Предположим, что у нас есть переменные ВладелецСсылка (ссылка на номенклатуру) и данные для цвета и размера, например, из таблицы значений или с формы. Давайте скорректируем код автора.
&НаСервере
Процедура ПрограммноСоздатьХарактеристикиСДопРеквизитами(ВладелецСсылка)
// Предполагаем, что ВладелецСсылка уже содержит ссылку на нужную номенклатуру
// Включим константу использования дополнительных реквизитов, если не включена
Если Не Константы.ИспользоватьДополнительныеРеквизитыИСведения.Получить() Тогда
Константы.ИспользоватьДополнительныеРеквизитыИСведения.Установить(Истина);
КонецЕсли;
// --- 1. Подготовка данных: Получение значений из формы или источника ---
// Предполагаем, что у нас есть ТЗ с формы, как у автора, но мы сделаем ее более универсальной
// Для демонстрации, создадим имитацию ТЗ с формы
ТЗНаФорме = Новый ТаблицаЗначений;
ТЗНаФорме.Колонки.Добавить("Свойство", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения"));
// Обратите внимание: Тип колонки "Значение" должен быть составным, чтобы принимать различные типы,
// в том числе ссылки на Справочник.ЗначенияСвойствОбъектов
ТЗНаФорме.Колонки.Добавить("Значение", Новый ОписаниеТипов("Строка, СправочникСсылка.ЗначенияСвойствОбъектов"));
// Пример заполнения ТЗНаФорме (обычно эти данные приходят с формы)
// Найдем определения свойств "Размер" и "Цвет"
СвойствоРазмерСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Размер");
СвойствоЦветСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Цвет");
Если Не ЗначениеЗаполнено(СвойствоРазмерСсылка) Тогда
Сообщить("Не найдено свойство 'Размер'. Проверьте его наличие в ПланахВидовХарактеристик.ДополнительныеРеквизитыИСведения.");
Возврат;
КонецЕсли;
Если Не ЗначениеЗаполнено(СвойствоЦветСсылка) Тогда
Сообщить("Не найдено свойство 'Цвет'. Проверьте его наличие в ПланахВидовХарактеристик.ДополнительныеРеквизитыИСведения.");
Возврат;
КонецЕсли;
// Пример значений (для отладки, в реальной системе они придут из ТЗ с формы)
// Для "Размер" (тип "Дополнительное значение")
ЗначениеРазмерS = НайтиСоздатьЗначениеДопРеквизита(СвойствоРазмерСсылка, "S");
ЗначениеРазмерM = НайтиСоздатьЗначениеДопРеквизита(СвойствоРазмерСсылка, "M");
ЗначениеРазмерL = НайтиСоздатьЗначениеДопРеквизита(СвойствоРазмерСсылка, "L");
// Для "Цвет" (тип "Дополнительное значение")
ЗначениеЦветКрасный = НайтиСоздатьЗначениеДопРеквизита(СвойствоЦветСсылка, "Красный");
ЗначениеЦветСиний = НайтиСоздатьЗначениеДопРеквизита(СвойствоЦветСсылка, "Синий");
// Имитация данных, которые могли бы прийти с формы
// Пусть у нас есть фиксированный цвет и несколько размеров
// Цвет
СтрокаЦвет = ТЗНаФорме.Добавить();
СтрокаЦвет.Свойство = СвойствоЦветСсылка;
СтрокаЦвет.Значение = ЗначениеЦветКрасный; // Важно: здесь ссылка на ЗначениеСвойствОбъектов!
// Размеры (каждый размер - это отдельная строка для ТЗНаФорме, если мы хотим получить их как Стр в цикле)
СтрокаРазмер1 = ТЗНаФорме.Добавить();
СтрокаРазмер1.Свойство = СвойствоРазмерСсылка;
СтрокаРазмер1.Значение = ЗначениеРазмерS;
СтрокаРазмер2 = ТЗНаФорме.Добавить();
СтрокаРазмер2.Свойство = СвойствоРазмерСсылка;
СтрокаРазмер2.Значение = ЗначениеРазмерM;
СтрокаРазмер3 = ТЗНаФорме.Добавить();
СтрокаРазмер3.Свойство = СвойствоРазмерСсылка;
СтрокаРазмер3.Значение = ЗначениеРазмерL;
// --- 2. Извлечение данных о цвете (аналогично коду автора) ---
// В данном случае, мы должны получить ссылку на Свойство 'Цвет' и его Значение
// из нашей ТЗНаФорме. Предположим, что цвет всегда один для партии.
СтрЦвет = Неопределено;
Для Каждого СтрокаТЗ Из ТЗНаФорме Цикл
Если СтрокаТЗ.Свойство = СвойствоЦветСсылка Тогда
СтрЦвет = СтрокаТЗ;
Прервать;
КонецЕсли;
КонецЦикла;
Если СтрЦвет = Неопределено Тогда
Сообщить("Необходимо выбрать свойство 'Цвет'.");
Возврат;
КонецЕсли;
// --- 3. Обход размеров и создание характеристик ---
Для Каждого СтрРазмер Из ТЗНаФорме Цикл
// Пропускаем строку с цветом, т.к. ее уже обработали
Если СтрРазмер.Свойство = СвойствоЦветСсылка Тогда
Продолжить;
КонецЕсли;
// Формируем наименование характеристики
// Важно: для наименования используем строковое представление значения
Наименование = Строка(СтрРазмер.Значение) + ", " + Строка(СтрЦвет.Значение);
// Проверяем, существует ли уже такая характеристика
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|"ХарактеристикиНоменклатуры.Ссылка КАК Ссылка
|ИЗ
| Справочник.ХарактеристикиНоменклатуры КАК ХарактеристикиНоменклатуры
|ГДЕ
| ХарактеристикиНоменклатуры.Владелец = &Владелец
| И ХарактеристикиНоменклатуры.Наименование = &Наименование";
Запрос.УстановитьПараметр("Наименование", Наименование);
Запрос.УстановитьПараметр("Владелец", ВладелецСсылка);
Если Запрос.Выполнить().Выбрать().Количество() <= 0 Тогда
// Если характеристика не найдена, создаем новую
Характеристика = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
Характеристика.Владелец = ВладелецСсылка;
Характеристика.Наименование = Наименование;
// --- КОРРЕКТНОЕ ДОБАВЛЕНИЕ ДОПОЛНИТЕЛЬНЫХ РЕКВИЗИТОВ ---
// Добавляем реквизит "Размер"
НоваяСтрокаРазмер = Характеристика.ДополнительныеРеквизиты.Добавить();
НоваяСтрокаРазмер.Свойство = СтрРазмер.Свойство; // Ссылка на ПВХ "Размер"
НоваяСтрокаРазмер.Значение = СтрРазмер.Значение; // Ссылка на ЗначенияСвойствОбъектов (например, "S")
// Добавляем реквизит "Цвет"
НоваяСтрокаЦвет = Характеристика.ДополнительныеРеквизиты.Добавить();
НоваяСтрокаЦвет.Свойство = СтрЦвет.Свойство; // Ссылка на ПВХ "Цвет"
НоваяСтрокаЦвет.Значение = СтрЦвет.Значение; // Ссылка на ЗначенияСвойствОбъектов (например, "Красный")
Попытка
Характеристика.Записать();
Сообщить("Характеристика '" + Наименование + "' успешно создана.");
Исключение ОшибкаЗаписи
Сообщить("Ошибка при записи новой характеристики '" + Наименование + "': " + ОшибкаЗаписи.Описание);
КонецПопытки;
Иначе
Сообщить("Характеристика '" + Наименование + "' уже существует. Пропускаем.");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Вспомогательная функция для поиска или создания значения дополнительного реквизита
// Возвращает ссылку на элемент справочника ЗначенияСвойствОбъектов
&НаСервере
Функция НайтиСоздатьЗначениеДопРеквизита(СвойствоСсылка, ЗначениеСтрока)
// СвойствоСсылка - ссылка на ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения (например, "Размер")
// ЗначениеСтрока - строковое представление значения (например, "S")
// Проверим тип значения самого доп. реквизита
Если СвойствоСсылка.ТипЗначения.СодержитТип(Тип("СправочникСсылка.ЗначенияСвойствОбъектов")) Тогда
// Если реквизит ожидает ссылку на ЗначенияСвойствОбъектов, то ищем/создаем его
ЗначениеОбъектовСсылка = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию(ЗначениеСтрока, Истина); // Искать по наименованию, иерархически
Если ЗначениеОбъектовСсылка = Неопределено Тогда
// Если не нашли, создадим новое значение
НовоеЗначение = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
НовоеЗначение.Наименование = ЗначениеСтрока;
НовоеЗначение.Владелец = СвойствоСсылка; // Важно: привязываем значение к свойству
Попытка
НовоеЗначение.Записать();
ЗначениеОбъектовСсылка = НовоеЗначение.Ссылка;
Исключение ОшибкаСоздания
Сообщить("Ошибка при создании нового значения дополнительного реквизита '" + ЗначениеСтрока + "': " + ОшибкаСоздания.Описание);
Возврат Неопределено;
КонецПопытки;
КонецЕсли;
Возврат ЗначениеОбъектовСсылка;
Иначе
// Если реквизит ожидает строку (или другой примитивный тип), просто возвращаем строку
Возврат ЗначениеСтрока;
КонецЕсли;
КонецФункции
Добавить() для табличной части Характеристика.ДополнительныеРеквизиты. Это гарантирует, что каждый реквизит будет добавлен в отдельную строку и не будет перезаписывать предыдущий.НайтиСоздатьЗначениеДопРеквизита проверяет тип значения дополнительного реквизита.СправочникСсылка.ЗначенияСвойствОбъектов, функция сначала пытается найти существующий элемент в справочнике Справочник.ЗначенияСвойствОбъектов по наименованию.Владелец создаваемого элемента ссылку на ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения, к которому это значение относится.НоваяСтрокаРазмер.Значение и НоваяСтрокаЦвет.Значение) теперь передаются ссылки на элементы справочника Справочник.ЗначенияСвойствОбъектов, а не простые строки.Новый ОписаниеТипов("Строка, СправочникСсылка.ЗначенияСвойствОбъектов"). Это позволит ей корректно хранить как строковые значения (если реквизит типа Строка), так и ссылки (если реквизит типа "Дополнительное значение").Строка(), чтобы получить текстовое представление ссылки на значение, например: Строка(СтрРазмер.Значение).Константы.ИспользоватьДополнительныеРеквизитыИСведения, это базовое условие для работы с механизмом.Данный подход обеспечивает корректное взаимодействие с механизмом дополнительных реквизитов 1С, позволяя гибко создавать и заполнять характеристики номенклатуры с учетом всех их свойств, включая те, что используют предопределенные значения. Теперь вы можете программно автоматизировать процесс создания характеристик для целого размерного ряда и цветовой палитры, значительно упростив работу с большими объемами данных.