При интеграции 1С с интернет-магазинами, маркетплейсами или внешними CRM-системами разработчики часто сталкиваются с необходимостью программной записи дополнительных реквизитов номенклатуры. На первый взгляд задача кажется тривиальной: получить объект номенклатуры, обратиться к табличной части дополнительных реквизитов и записать туда нужные значения. Однако на практике разработчики часто сталкиваются с ситуацией, когда код выполняется без ошибок, но данные в интерфейсе 1С не сохраняются или не обновляются.
В этой статье мы подробно разберем, как устроена «физика» хранения дополнительных свойств в системе 1С:Предприятие, разберем типичные ошибки начинающих разработчиков и рассмотрим два надежных способа программной работы с ними: ручное заполнение табличной части и профессиональное использование программного интерфейса (API) Библиотеки стандартных подсистем (БСП).
Прежде чем переходить к написанию кода, давайте разберем структуру хранения этих данных в базе данных 1С. Механизм свойств, разработанный в БСП, делит сопутствующую информацию на две принципиально разные категории:
ДополнительныеРеквизиты), жестко связанной с основной таблицей справочника (например, Справочник.Номенклатура) по уникальному идентификатору (GUID) родительского элемента. Запись дополнительного реквизита всегда требует получения объекта для редактирования (вызова метода ПолучитьОбъект()) и накладывает блокировку на всю карточку товара.РегистрСведений.ДополнительныеСведения. Измерениями регистра выступают ссылка на Объект (номенклатуру) и ссылка на Свойство (из Плана видов характеристик), а ресурсом — непосредственно Значение. Сведения можно изменять и записывать напрямую через менеджер записи регистра без блокировки и перезаписи самого элемента номенклатуры.Часто у разработчиков возникает резонный вопрос: «Я открываю новую карточку товара в 1С, перехожу на вкладку дополнительных реквизитов и уже вижу там пустые поля для заполнения. Означает ли это, что строки в базе данных уже созданы?»
Нет, физически в базе данных этих строк еще не существует. Платформа 1С динамически генерирует элементы интерфейса при открытии формы. Она обращается к настройкам системы, определяет «Вид номенклатуры» открытого товара, находит связанный с ним набор свойств в справочнике НаборыДополнительныхРеквизитовИСведений и отображает пустые поля ввода на форме. В базу данных (в табличную часть объекта) строка запишется только тогда, когда пользователь или программный код заполнит это поле конкретным значением. Это сделано для экономии дискового пространства СУБД и оптимизации производительности.
Проанализируем классические ошибки, которые приводят к тому, что код выполняется без ошибок, но реквизиты не сохраняются:
Свойство (ссылка на элемент Плана видов характеристик ДополнительныеРеквизитыИСведения) и Значение (само значение реквизита). Если программно добавить строку в табличную часть, присвоить ей только значение, но оставить поле Свойство пустым, система при записи объекта просто проигнорирует или очистит эту строку.НайтиПоНаименованию("ИмяРеквизита") часто возвращает пустую ссылку. Дело в том, что в БСП наименования свойств для разных видов номенклатуры могут содержать уточняющие суффиксы в скобках, например, "ИдентификаторИМ (Товар для ИМ)" вместо лаконичного "ИдентификаторИМ".ПолучитьСсылку(Новый УникальныйИдентификатор(Идентификатор1С)) генерирует ссылку исключительно в оперативной памяти платформы. Он никогда не возвращает значение Неопределено, даже если элемента с таким GUID физически нет в базе данных. Проверять существование объекта в базе данных таким образом нельзя.Записать() ровно один раз в конце процедуры.Рассмотрим детальный и оптимизированный алгоритм ручной работы с табличной частью ДополнительныеРеквизиты. Нам необходимо сначала найти ссылки на нужные свойства в Плане видов характеристик, проверить наличие элемента номенклатуры в базе данных, получить объект для изменения, обойти строки табличной части для обновления существующих записей или добавить новые строки, если свойства ранее не заполнялись.
Для поиска свойств в ПВХ рекомендуется использовать поиск по реквизиту Имя (это уникальное имя свойства для разработчиков, которое не меняется пользователями из интерфейса), либо осуществлять точный поиск по наименованию с учетом владельца свойства.
Ниже представлен готовый к использованию код процедуры:
&НаСервере
Процедура ЗаписатьДопРеквизитыНоменклатурыВручную(Идентификатор1С, ИдентификаторИМ, НаименованиеИМ)
// 1. Находим ссылки на свойства в Плане видов характеристик
СвойствоID = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ИдентификаторИМ (Товар для ИМ)", Истина);
СвойствоName = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("НаименованиеИМ (Товар для ИМ)", Истина);
Если СвойствоID.Пустая() Или СвойствоName.Пустая() Тогда
ВызватьИсключение "В системе не настроены необходимые дополнительные реквизиты для товаров ИМ!";
КонецЕсли;
// 2. Получаем ссылку на номенклатуру по UUID
СсылкаНаНоменклатуру = Справочники.Номенклатура.ПолучитьСсылка(Новый УникальныйИдентификатор(Идентификатор1С));
// Надежная проверка существования элемента в базе данных
Если ОбщегоНазначения.СсылкаСуществует(СсылкаНаНоменклатуру) Тогда
// Получаем объект для редактирования
НоменклатураОбъект = СсылкаНаНоменклатуру.ПолучитьОбъект();
// Переменные-флаги для отслеживания необходимости добавления новых строк
НужноДобавитьID = Истина;
НужноДобавитьName = Истина;
// 3. Обходим существующую табличную часть дополнительных реквизитов
Для Каждого СтрокаРеквизита Из НоменклатураОбъект.ДополнительныеРеквизиты Цикл
Если СтрокаРеквизита.Свойство = СвойствоID Тогда
СтрокаРеквизита.Значение = ИдентификаторИМ;
НужноДобавитьID = Ложь;
ИначеЕсли СтрокаРеквизита.Свойство = СвойствоName Тогда
СтрокаРеквизита.Значение = НаименованиеИМ;
НужноДобавитьName = Ложь;
КонецЕсли;
КонецЦикла;
// 4. Если свойства не были найдены в табличной части, добавляем новые строки
Если НужноДобавитьID Тогда
НоваяСтрока = НоменклатураОбъект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = СвойствоID;
НоваяСтрока.Значение = ИдентификаторИМ;
КонецЕсли;
Если НужноДобавитьName Тогда
НоваяСтрока = НоменклатураОбъект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = СвойствоName;
НоваяСтрока.Значение = НаименованиеИМ;
КонецЕсли;
// 5. Записываем измененный объект номенклатуры один раз
Попытка
НоменклатураОбъект.Записать();
Сообщить("Дополнительные реквизиты для номенклатуры успешно сохранены.");
Исключение
ЗаписьЖурналаРегистрации("СинхронизацияСИМ", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение "Не удалось записать объект номенклатуры: " + ОписаниеОшибки();
КонецПопытки;
Иначе
Сообщить("Номенклатура с указанным уникальным идентификатором не найдена в базе данных!");
КонецЕсли;
КонецПроцедуры
Прямая модификация табличных частей объектов — рабочий метод, но он заставляет разработчика самостоятельно писать громоздкий код проверок, поиска дублей свойств, блокировок данных и обработки исключений. В современных конфигурациях 1С (УТ 11, ERP, Комплексная автоматизация, УНФ), построенных на базе БСП, реализован специализированный программный интерфейс, который делает эту работу за вас.
Для записи свойств используется общий модуль УправлениеСвойствами и его ключевой метод ЗаписатьСвойстваУОбъекта(). Этот метод берет на себя всю «рутину»:
Проанализируем пример реализации нашей задачи с помощью БСП. Код становится лаконичным, легко читаемым и надежно защищенным от будущих обновлений конфигурации.
&НаСервере
Процедура ЗаписатьДопРеквизитыЧерезБСП(СсылкаНаНоменклатуру, ИдентификаторИМ, НаименованиеИМ)
// 1. Находим нужные свойства в Плане видов характеристик.
// Рекомендуется искать свойства по реквизиту "Имя" (имя для разработчиков).
СвойствоID = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "ИдентификаторИМ");
СвойствоName = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "НаименованиеИМ");
// Если по коду/имени не нашли, пробуем точный поиск по наименованию
Если СвойствоID.Пустая() Тогда
СвойствоID = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ИдентификаторИМ (Товар для ИМ)", Истина);
КонецЕсли;
Если СвойствоName.Пустая() Тогда
СвойствоName = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("НаименованиеИМ (Товар для ИМ)", Истина);
КонецЕсли;
// Если свойства отсутствуют в системе - прерываем выполнение
Если СвойствоID.Пустая() Или СвойствоName.Пустая() Тогда
ЗаписьЖурналаРегистрации("СвойстваНоменклатуры", УровеньЖурналаРегистрации.Предупреждение, , , "Не найдены метаданные дополнительных реквизитов.");
Возврат;
КонецЕсли;
// 2. Формируем таблицу значений со свойствами и их новыми значениями
ТаблицаСвойств = Новый ТаблицаЗначений;
ТаблицаСвойств.Колонки.Добавить("Свойство", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения"));
ТаблицаСвойств.Колонки.Добавить("Значение"); // Тип произвольный, зависит от настроек реквизита
// Заполняем таблицу парами Свойство-Значение
НоваяСтрока = ТаблицаСвойств.Добавить();
НоваяСтрока.Свойство = СвойствоID;
НоваяСтрока.Значение = ИдентификаторИМ;
НоваяСтрока = ТаблицаСвойств.Добавить();
НоваяСтрока.Свойство = СвойствоName;
НоваяСтрока.Значение = НаименованиеИМ;
// 3. Вызываем стандартный метод БСП для записи свойств у владельца.
// Параметры: Ссылка на объект (Владелец) и подготовленная ТаблицаЗначений.
Попытка
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаНоменклатуру, ТаблицаСвойств);
Сообщить("Свойства успешно записаны средствами БСП.");
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка сохранения дополнительных реквизитов: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В дополнение к записи БСП предоставляет удобные функции для быстрого и безопасного чтения значений свойств без написания тяжелых ручных запросов к табличным частям номенклатуры. Выясним, какими функциями общего модуля УправлениеСвойствами мы можем воспользоваться:
УправлениеСвойствами.ЗначениеСвойства(ВладелецСвойств, Свойство) — возвращает значение конкретного свойства для переданного объекта (номенклатуры, документа, контрагента). Если свойство не заполнено, возвращается пустая ссылка или пустое значение соответствующего типа.УправлениеСвойствами.ЗначенияСвойств(ВладелецСвойств) — возвращает все заполненные дополнительные реквизиты и сведения объекта в виде структуры, где ключами являются ссылки на свойства (или их имена разработчика), а значениями — их установленные в системе значения.Посмотрим на пример использования этих функций:
// Пример получения значения одного конкретного свойства номенклатуры
ЗначениеИдентификатора = УправлениеСвойствами.ЗначениеСвойства(СсылкаНаНоменклатуру, СвойствоID);
Если ЗначениеЗаполнено(ЗначениеИдентификатора) Тогда
Сообщить("У товара уже установлен ID интернет-магазина: " + ЗначениеИдентификатора);
КонецЕсли;
Для создания отказоустойчивых интеграционных решений в среде 1С мы рекомендуем придерживаться следующих правил:
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта() сократит объем вашего кода, убережет систему от логических ошибок параллельной работы пользователей и гарантирует корректность сохранения данных независимо от того, реквизитом или сведением является настраиваемый параметр.Свойство ссылкой на элемент Плана видов характеристик и не забывайте вызывать метод Записать() у самого объекта.НайтиПоНаименованию()), если пользователи могут изменить их название в режиме «1С:Предприятие». Старайтесь задействовать поиск по уникальному программному имени свойства (реквизит Имя).