При начальном заполнении информационных баз или тестировании функционала разработчикам часто требуется пакетно создать тестовые данные. Типичный сценарий — программное создание позиций номенклатуры вместе с базовыми единицами измерения. Рассмотрим, как правильно реализовать эту задачу на встроенном языке 1С:Предприятие, разберем возможные подходы и выясним технические нюансы связывания взаимозависимых объектов.
Прежде чем приступать к написанию кода, проанализируем структуру используемой конфигурации. В зависимости от архитектуры прикладного решения справочники единиц измерения и номенклатуры могут быть связаны по-разному:
ЕдиницыИзмерения подчинен справочнику Номенклатура. Это означает, что единица не может существовать без заполненного реквизита Владелец. В свою очередь, сама номенклатура требует ссылки на базовую единицу хранения остатков. Возникает циклическая зависимость ссылок.УпаковкиЕдиницыИзмерения или КлассификаторЕдиницИзмерения). В таком случае плодить 50 одинаковых единиц не имеет смысла — достаточно один раз найти нужную единицу (например, «шт») и подставлять ее ссылку в каждый новый элемент номенклатуры.Рассмотрим ситуацию, когда необходимо создать именно 50 взаимосвязанных пар объектов, где единица подчинена номенклатуре.
Разберем самый эффективный способ создания взаимозависимых элементов. Основная сложность циклической связи: чтобы записать единицу измерения, нам уже нужна ссылка на номенклатуру-владельца, а чтобы записать номенклатуру — нужна ссылка на единицу. Решить эту дилемму без лишних повторных обращений к базе данных позволяет метод УстановитьСсылкуНового().
Посмотрим на пример кода пакетной генерации 50 элементов в цикле Для:
&НаСервере
Процедура СоздатьНоменклатуруИЕдиницы()
Для Ном = 1 По 50 Цикл
// Получаем будущие ссылки для обоих объектов
СсылкаНоменклатура = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор());
СсылкаЕдиница = Справочники.ЕдиницыИзмерения.ПолучитьСсылку(Новый УникальныйИдентификатор());
// Формируем объект единицы измерения
ОбъектЕдиница = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
ОбъектЕдиница.УстановитьСсылкуНового(СсылкаЕдиница);
ОбъектЕдиница.Наименование = "шт";
ОбъектЕдиница.Владелец = СсылкаНоменклатура;
ОбъектЕдиница.Записать();
// Формируем объект номенклатуры
ОбъектНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
ОбъектНоменклатура.УстановитьСсылкуНового(СсылкаНоменклатура);
ОбъектНоменклатура.Наименование = "Тестовый товар " + Строка(Ном);
ОбъектНоменклатура.ЕдиницаХраненияОстатков = СсылкаЕдиница;
ОбъектНоменклатура.Записать();
КонецЦикла;
КонецПроцедуры
Разберем по шагам, как работает данная конструкция:
Справочники.<ИмяСправочника>.ПолучитьСсылку() с новым УникальныйИдентификатор() генерируются ссылки, которые еще физически не сохранены в таблицах СУБД.УстановитьСсылкуНового() связывает создаваемый в оперативной памяти объект с заранее зарезервированной ссылкой. Это позволяет немедленно передать СсылкаНоменклатура в реквизит Владелец подчиненного справочника.Если нет необходимости использовать метод УстановитьСсылкуНового(), можно применить классический двухэтапный алгоритм. Проанализируем этот сценарий:
Владелец полученную ссылку ОбъектНоменклатура.Ссылка и записываем единицу.ЕдиницаХраненияОстатков) ссылкой на сохраненную единицу измерения и вызываем Записать() повторно.Посмотрим на реализацию в коде:
&НаСервере
Процедура СоздатьНоменклатуруДвухэтапно()
Для Ном = 1 По 50 Цикл
// Шаг 1. Первичная запись номенклатуры для получения ссылки
ОбъектНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
ОбъектНоменклатура.Наименование = "Товар " + Строка(Ном);
ОбъектНоменклатура.Записать();
// Шаг 2. Создание подчиненной единицы
ОбъектЕдиница = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
ОбъектЕдиница.Наименование = "шт";
ОбъектЕдиница.Владелец = ОбъектНоменклатура.Ссылка;
ОбъектЕдиница.Записать();
// Шаг 3. Дозаполнение номенклатуры и повторная запись
ОбъектНоменклатура.ЕдиницаХраненияОстатков = ОбъектЕдиница.Ссылка;
ОбъектНоменклатура.Записать();
КонецЦикла;
КонецПроцедуры
Этот способ проще в отладке, однако выполняет две операции записи номенклатуры вместо одной, что при больших объемах данных отражается на скорости работы.
В дискуссиях разработчиков иногда ради шутки или эксперимента предлагается создавать элементы через рекурсивный вызов процедуры со счетчиком. Рассмотрим подробнее, почему на практике так делать категорически нельзя.
В платформе 1С:Предприятие глубина стека вызовов имеет строгий лимит. Если для 50 элементов рекурсия еще отработает, то при увеличении объема до 1000–2000 вызовов произойдет аппаратное переполнение стека (Stack Overflow). Это приведет к аварийному завершению сеанса или падению рабочего процесса сервера 1С, причем перехватить подобную системную ошибку через блок Попытка ... Исключение невозможно. Любая массовая генерация данных должна реализовываться исключительно через циклические конструкции Для или Пока.
Выясним полезные приемы, которые сделают программное создание справочников надежным:
НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это кардинально ускоряет процесс за счет объединения дисковых операций СУБД.Объект.ОбменДанными.Загрузка = Истина. Это отключит прикладные проверки и ускорит генерацию.Код строковыми значениями вручную, если в справочнике включена автоматическая нумерация, чтобы не сбить встроенный механизм генерации номеров платформы.