При разработке обработок загрузки номенклатуры из внешних источников (например, из файлов Excel) часто возникает задача записи дополнительных реквизитов и характеристик товара. Нередко разработчики сталкиваются с ситуацией, когда для вновь создаваемых позиций свойства записываются успешно, а у уже существующих элементов в базе данные не обновляются либо перезаписываются с ошибками. Проанализируем причины такого поведения и разберем корректные способы решения этой задачи в конфигурациях на обычных формах, таких как «1С:Управление торговлей 10.3».
Рассмотрим распространенные ошибки, которые приводят к сбоям при попытке обновить свойства номенклатуры:
СоздатьНаборЗаписей(), установить отбор по объекту и просто добавить новую строку без предварительного чтения существующих данных, при вызове метода Записать(Истина) все прежние свойства номенклатуры будут безвозвратно удалены, а запишется только новое. Если же передать параметр Ложь (или не учесть режим замещения), при наличии уже существующей записи возникнет конфликт ключевых полей.Попытка ... Исключение КонецПопытки без вывода текста ошибки (ОписаниеОшибки()) маскирует проблему, создавая ложное впечатление, что код отработал штатно, хотя запись на самом деле была отклонена СУБД.СвойстваОбъектов для каждого реквизита жестко задан тип значения. Если для свойства определен тип «Строка» или «Число», а в регистр передается ссылка на справочник ЗначенияСвойствОбъектов (или наоборот), платформа вызовет исключение при несоответствии типов.ЗначенияСвойствОбъектов является подчиненным плану видов характеристик СвойстваОбъектов. Вызов метода НайтиПоНаименованию без указания владельца свойства может вернуть элемент с совпадающим названием, принадлежащий совершенно другому свойству.Регистр сведений ЗначенияСвойствОбъектов является непериодическим и независимым. Его ключевыми измерениями выступают Объект (ссылка на номенклатуру) и Свойство (ссылка на элемент плана видов характеристик). Для точечной перезаписи конкретного свойства удобнее всего использовать СоздатьМенеджерЗаписи(). В этом случае не требуется вычитывать весь список свойств позиции номенклатуры.
Разберем по шагам реализацию через менеджер записи:
Функция ЗаписатьСвойствоТовара(СсылкаНоменклатура, СсылкаСвойство, ЗначениеДляЗаписи)
Если Не ЗначениеЗаполнено(СсылкаНоменклатура) Или Не ЗначениеЗаполнено(СсылкаСвойство) Тогда
Возврат Ложь;
КонецЕсли;
МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = СсылкаНоменклатура;
МенеджерЗаписи.Свойство = СсылкаСвойство;
МенеджерЗаписи.Значение = ЗначениеДляЗаписи;
Попытка
// Параметр Истина гарантирует замещение записи при ее наличии
МенеджерЗаписи.Записать(Истина);
Возврат Истина;
Исключение
Сообщить("Ошибка при записи свойства: " + ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Посмотрим на практический пример использования данной функции при обработке строковых свойств (например, кода производителя или артикула):
// Оптимизация: находим свойство заранее до входа в цикл обработки строк
ПланСвойств = ПланыВидовХарактеристик.СвойстваОбъектов;
СвойствоКод = ПланСвойств.НайтиПоНаименованию("Код производителя, MPN");
Если ЗначениеЗаполнено(СвойствоКод) Тогда
ЗначениеКода = СокрЛП(ТекущаяСтрокаТаблицы.ПартНомер);
Если ЗначениеЗаполнено(ЗначениеКода) Тогда
ЗаписатьСвойствоТовара(ТекНоменклатура, СвойствоКод, ЗначениеКода);
КонецЕсли;
Иначе
Сообщить("Свойство 'Код производителя, MPN' не найдено в базе данных!");
КонецЕсли;
Если по архитектурным соображениям требуется групповая запись свойств через РегистрСведенийНаборЗаписей, необходимо обязательно прочитать существующие свойства номенклатуры, модифицировать нужные строки или добавить отсутствующие, и только после этого выполнять запись.
Рассмотрим подробнее программный код такого подхода:
Процедура ОбновитьСвойствоЧерезНабор(ТекНом, СсылкаСвойство, НовоеЗначение)
НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(ТекНом);
// Считываем текущие свойства данного объекта из базы
НаборЗаписей.Прочитать();
СтрокаСвойства = Неопределено;
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.Свойство = СсылкаСвойство Тогда
СтрокаСвойства = Запись;
Прервать;
КонецЕсли;
КонецЦикла;
// Если свойство еще не было задано, добавляем новую запись в набор
Если СтрокаСвойства = Неопределено Тогда
СтрокаСвойства = НаборЗаписей.Добавить();
СтрокаСвойства.Объект = ТекНом;
СтрокаСвойства.Свойство = СсылкаСвойство;
КонецЕсли;
СтрокаСвойства.Значение = НовоеЗначение;
Попытка
// Записываем обновленный набор с замещением
НаборЗаписей.Записать(Истина);
Исключение
Сообщить("Не удалось обновить свойства номенклатуры: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Выясним причину ошибок при работе со свойствами, имеющими тип значения СправочникСсылка.ЗначенияСвойствОбъектов. Для таких свойств нельзя передавать обычную строку — в регистре должна храниться ссылка на элемент подчиненного справочника.
Посмотрим, как правильно организовать поиск и создание нового значения с обязательным указанием владельца:
Функция ПолучитьСсылкуНаЗначениеСвойства(СсылкаСвойство, ТекстЗначения)
Если Не ЗначениеЗаполнено(ТекстЗначения) Тогда
Возврат Справочники.ЗначенияСвойствОбъектов.ПустаяСсылка();
КонецЕсли;
// Выполняем поиск строго в рамках нужного свойства-владельца
НайденноеЗначение = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию(
ТекстЗначения,
Истина,
,
СсылкаСвойство
);
Если НайденноеЗначение.Пустая() Тогда
НовыйЭлемент = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
НовыйЭлемент.Владелец = СсылкаСвойство;
НовыйЭлемент.Наименование = ТекстЗначения;
Попытка
НовыйЭлемент.Записать();
Возврат НовыйЭлемент.Ссылка;
Исключение
Сообщить("Ошибка добавления значения свойства: " + ОписаниеОшибки());
Возврат Справочники.ЗначенияСвойствОбъектов.ПустаяСсылка();
КонецПопытки;
КонецЕсли;
Возврат НайденноеЗначение;
КонецФункции
После получения ссылки на элемент подчиненного справочника мы передаем ее в качестве значения в менеджер записи или набор записей, как было показано в предыдущих примерах.
Для обеспечения стабильной и быстрой работы обработки придерживайтесь следующих правил:
НайтиПоНаименованию для планов видов характеристик внутри цикла по строкам загружаемого документа. Выполните поиск нужных свойств один раз до начала обхода строк и сохраните ссылки в переменные.Число(), предварительно очистив строку от пробелов и заменив разделители дроби.Номенклатура и выполняться независимо от того, был ли товар только что создан или найден в базе как существующий.