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