При разработке универсальных решений в 1С, особенно связанных с динамической обработкой данных или интеграциями, часто возникает необходимость программно создать объект определенного прикладного типа (например, элемент справочника или новый документ), зная только его Тип. На первый взгляд, кажется логичным использовать оператор Новый(Тип), но, как выясняется, этот подход имеет свои особенности и ограничения, особенно для ссылочных объектов, таких как СправочникОбъект или ДокументОбъект.
Давайте подробнее рассмотрим эту проблему, выясним, почему прямое использование Новый(Тип) для прикладных объектов не работает так, как ожидается, и предложим эффективные решения.
Один из первых инстинктов программиста при необходимости создать объект по типу — это использование конструкции Новый(Тип). Автор темы столкнулся именно с этим: Новый(Тип) действительно создает некий объект, но с ним невозможно полноценно работать – его нельзя записать, заполнить реквизиты, и он ведет себя как "пустой" или "неинициализированный".
Почему так происходит?
СправочникОбъект, ДокументОбъект, ПланВидовХарактеристикОбъект и т.д., оператор Новый(Тип) действительно возвращает объект нужного типа. Однако, этот объект не является полноценно инициализированным. Это своего рода "заготовка", с которой нельзя выполнять операции записи или проведения, потому что внутренние механизмы платформы не считают его готовым к работе. Для этих типов требуется вызов специализированных методов-конструкторов, которые предоставляют менеджеры объектов.Число, Строка, Дата, Булево) конструкция Новый(Тип) вообще вызывает исключение. Примитивные типы являются типами-значениями и создаются путем прямого присвоения литералов или значений других переменных (например, МояСтрока = "Привет", МоеЧисло = 123, МояДата = ТекущаяДата()). У них нет конструкторов в привычном смысле для оператора Новый.Новый(Тип) отлично работает для создания многих системных объектов и коллекций, которые не требуют сложной инициализации. Например:
МойМассив = Новый Массив; // или Новый(Тип("Массив"))
МояСтруктура = Новый Структура; // или Новый(Тип("Структура"))
МоеСоответствие = Новый Соответствие; // или Новый(Тип("Соответствие"))
МойИнтернетПрокси = Новый ИнтернетПрокси;
Новый(Тип) выполняет роль полноценного конструктора.Новый(Тип) также играет важную роль в динамическом получении менеджеров прикладных объектов, что является ключом к решению нашей задачи.Итак, проблема заключается в том, что для прикладных объектов, таких как справочники и документы, платформа 1С предоставляет другой механизм создания – через их менеджеры.
Для создания полноценных, инициализированных объектов прикладных типов (таких как элементы справочника, документы, наборы записей регистров и т.д.) необходимо использовать специальные объекты-менеджеры. Каждый прикладной объект в 1С имеет свой соответствующий менеджер, например, для СправочникОбъект.Товары существует СправочникМенеджер.Товары, для ДокументОбъект.РеализацияТоваровУслуг — ДокументМенеджер.РеализацияТоваровУслуг.
Эти менеджеры предоставляют методы для создания новых объектов: СоздатьЭлемент() для справочников, СоздатьДокумент() для документов, СоздатьНаборЗаписей() для регистров сведений и накопления, СоздатьВид() для планов видов характеристик и т.д. Эти методы не просто создают объект, но и выполняют его необходимую инициализацию, заполняя значениями по умолчанию (например, дату документа текущей датой).
Чтобы получить менеджер объекта динамически, зная только его тип, мы можем воспользоваться несколькими подходами:
Новый():
Этот способ является наиболее распространенным и гибким. Мы сначала получаем объект метаданных для нужного типа, а затем, зная полное имя менеджера (которое отличается от имени объекта суффиксом "Менеджер" вместо "Объект"), используем оператор Новый() для создания экземпляра менеджера.
Рассмотрим пошагово:
ТипОбъекта, содержащая Тип("СправочникОбъект.Валюты").Метаданные.НайтиПоТипу(ТипОбъекта).
ТипОбъекта = Тип("СправочникОбъект.Валюты");
МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипОбъекта);
// Пример: "СправочникОбъект.Валюты" -> "СправочникМенеджер.Валюты"
ИмяМенеджера = СтрЗаменить(МетаданныеОбъекта.ПолноеИмя, "Объект.", "Менеджер.");
Новый() с полученным именем менеджера.
МенеджерОбъекта = Новый(ИмяМенеджера);
Объединим это в один универсальный фрагмент кода:
// Предположим, у нас есть Тип (например, полученный из реквизита формы или где-то еще)
ТипДляСоздания = Тип("СправочникОбъект.Номенклатура");
// 1. Получаем объект метаданных по типу
МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипДляСоздания);
// Проверяем, что это не примитивный тип и не тип, не имеющий менеджера
Если МетаданныеОбъекта = Неопределено Тогда
Сообщить("Для типа " + ТипДляСоздания + " не удалось получить метаданные или он не поддерживается.");
Возврат;
КонецЕсли;
// 2. Формируем полное имя менеджера
// Например: "СправочникОбъект.Номенклатура" -> "СправочникМенеджер.Номенклатура"
// или "ДокументОбъект.РеализацияТоваровУслуг" -> "ДокументМенеджер.РеализацияТоваровУслуг"
ИмяТипаМенеджера = СтрЗаменить(МетаданныеОбъекта.ПолноеИмя, "Объект.", "Менеджер.");
// 3. Создаем экземпляр менеджера
МенеджерОбъекта = Новый(ИмяТипаМенеджера);
// Теперь МенеджерОбъекта содержит нужный менеджер,
// например, СправочникМенеджер.Номенклатура или ДокументМенеджер.РеализацияТоваровУслуг
// и мы можем использовать его методы для создания объектов.
В некоторых конфигурациях 1С (например, в БСП) существуют общие модули с функциями, которые упрощают получение менеджера объекта по типу. Например, функция ОбщегоНазначения.ПолучитьМенеджерОбъектаПоТипу(Тип) (точное название может варьироваться в зависимости от конфигурации).
Использование такой функции значительно упрощает код:
// Предположим, у нас есть Тип
ТипДляСоздания = Тип("ДокументОбъект.ПриходнаяНакладная");
// Используем универсальную функцию (если доступна)
Попытка
МенеджерОбъекта = ОбщегоНазначения.ПолучитьМенеджерОбъектаПоТипу(ТипДляСоздания);
Исключение
Сообщить("Ошибка получения менеджера объекта через ОбщегоНазначения: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Если МенеджерОбъекта = Неопределено Тогда
Сообщить("Не удалось получить менеджер для типа: " + ТипДляСоздания);
Возврат;
КонецЕсли;
// Дальнейшая работа с МенеджерОбъекта
Всегда стоит проверять наличие таких вспомогательных функций в вашей конфигурации перед тем, как реализовывать логику получения менеджера вручную.
После того как мы получили нужный менеджер объекта, процесс создания становится простым и интуитивно понятным:
СоздатьЭлемент().
// Предполагаем, что МенеджерОбъекта - это СправочникМенеджер
НовыйЭлементСправочника = МенеджерОбъекта.СоздатьЭлемент();
// Теперь НовыйЭлементСправочника готов к заполнению и записи
НовыйЭлементСправочника.Наименование = "Новый товар " + Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy ЧЧ:мм:сс");
НовыйЭлементСправочника.Родитель = Справочники.ГруппыНоменклатуры.НайтиПоНаименованию("Основные товары");
НовыйЭлементСправочника.Записать();
Сообщить("Создан элемент справочника: " + НовыйЭлементСправочника.Наименование);
СоздатьДокумент().
// Предполагаем, что МенеджерОбъекта - это ДокументМенеджер
НовыйДокумент = МенеджерОбъекта.СоздатьДокумент();
// НовыйДокумент уже будет иметь заполненные реквизиты по умолчанию (например, Дата)
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.МояОрганизация;
// Заполняем табличную часть
НоваяСтрока = НовыйДокумент.Товары.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 100;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
НовыйДокумент.Записать(); // Для проведения можно использовать НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Создан документ: " + НовыйДокумент.Вид() + " №" + НовыйДокумент.Номер + " от " + НовыйДокумент.Дата);
СоздатьНаборЗаписей().
// Предполагаем, что МенеджерОбъекта - это РегистрСведенийМенеджер или РегистрНакопленияМенеджер
НаборЗаписей = МенеджерОбъекта.СоздатьНаборЗаписей();
// НаборЗаписей.Отбор.Период.Установить(ТекущаяДата()); // Пример отбора для регистров сведений
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Измерение1 = "Значение1";
НоваяЗапись.Ресурс1 = 123;
НаборЗаписей.Записать();
Сообщить("Записаны записи регистра: " + МенеджерОбъекта.Имя());
СоздатьВид().
// Предполагаем, что МенеджерОбъекта - это ПланВидовХарактеристикМенеджер
НовыйВид = МенеджерОбъекта.СоздатьВид();
НовыйВид.Наименование = "Новая характеристика";
НовыйВид.ТипЗначения = Новый ОписаниеТипов("Число");
НовыйВид.Записать();
Сообщить("Создан вид характеристики: " + НовыйВид.Наименование);
Важным преимуществом создания объектов через методы менеджеров (СоздатьЭлемент(), СоздатьДокумент() и др.) является то, что возвращаемые объекты уже инициализированы. Это означает, что они уже имеют некоторые значения по умолчанию (например, Дата для документов, ПометкаУдаления для справочников) и готовы к дальнейшему заполнению реквизитов и записи в базу данных. Вам не нужно выполнять дополнительную "инициализацию", как в случае с пустым объектом от Новый(Тип).
После того как вы заполнили необходимые реквизиты объекта, вы можете вызвать метод Записать() для сохранения его в базу данных. Для документов также доступен метод Записать(РежимЗаписиДокумента.Проведение) для проведения документа.
В некоторых специфических сценариях, особенно в управляемых формах, где требуется работать с объектом "на лету" без прямого создания в базе данных или с особыми требованиями к связыванию с формой, можно использовать механизм динамического добавления реквизитов формы. Этот подход позволяет создать реквизит формы указанного типа и затем получить его значение как объект.
Рассмотрим пример, приведенный в обсуждении:
&НаСервере
Процедура Команда1НаСервере()
// 1. Определяем полное имя типа прикладного объекта, который хотим создать
ИмяТипаПрикладногоОбъекта = "ДокументОбъект.Документ1"; // Например, "ДокументОбъект.ПоступлениеТоваровУслуг"
// 2. Создаем массив реквизитов формы для добавления
МассивРеквизитов = Новый Массив;
// 3. Добавляем новый реквизит формы с нужным типом
// ПрикладнойОбъект - это имя реквизита, которое будет использоваться на форме
МассивРеквизитов.Добавить(Новый РеквизитФормы("ПрикладнойОбъект", Новый ОписаниеТипов(ИмяТипаПрикладногоОбъекта)));
// 4. Изменяем реквизиты формы, добавляя новый
ИзменитьРеквизиты(МассивРеквизитов);
// 5. Получаем созданный объект из реквизита формы
// На этом этапе платформа создает и инициализирует объект указанного типа
ПрикладнойОбъект = РеквизитФормыВЗначение("ПрикладнойОбъект");
// 6. Теперь можно работать с ПрикладнойОбъект
ПрикладнойОбъект.Дата = ТекущаяДата();
ПрикладнойОбъект.Контрагент = Справочники.Контрагенты.НайтиПоНаименованию("Поставщик 1");
// ... заполняем другие реквизиты
// 7. Записываем объект в базу данных
// ПрикладнойОбъект.Записать();
// Сообщить("Документ " + ПрикладнойОбъект.Вид() + " создан и заполнен на форме.");
// Для демонстрации можно вывести что-то на клиент
// ЗначениеВРеквизитФормы(ПрикладнойОбъект, "ПрикладнойОбъект"); // Если нужно вернуть на клиент для отображения
Сообщить("Объект типа " + ПрикладнойОбъект.Метаданные().Имя + " создан и заполнен.");
КонецПроцедуры;
Этот подход достаточно сложен и применим преимущественно в контексте управляемых форм. Он позволяет "привязать" динамически созданный объект к форме, что может быть полезно для отладки, интерактивного заполнения или в ситуациях, когда объект должен быть частью данных формы, а не просто автономным программным объектом.
Важно: Даже при использовании ИзменитьРеквизиты(), для фактической записи объекта в базу данных все равно потребуется вызов метода Записать() у самого объекта, как и в случае создания через менеджер.
Автор темы упомянул, что его задача связана с универсальной загрузкой данных, где после выбора загружаемого объекта (справочник или документ) необходимо выводить его реквизиты для сопоставления с колонками внешнего файла. Для реквизитов составного типа, которые являются ссылками, требуется создавать или искать объекты по выбранному в колонке типу.
Описанный выше подход с использованием менеджеров объектов идеально подходит для этой задачи. Разберем, как мы можем применить его по шагам:
ТипОбъектаДляЗагрузки = Тип("СправочникОбъект.Номенклатура");.МенеджерОсновногоОбъекта.Если мы создаем новый объект:
НовыйОбъект = МенеджерОсновногоОбъекта.СоздатьЭлемент(); // или СоздатьДокумент()
Если мы ищем существующий объект по какому-либо уникальному полю (например, коду или наименованию), то используем методы менеджера типа НайтиПоКоду(), НайтиПоНаименованию() или НайтиПоРеквизиту().
Тип("СправочникОбъект.ЕдиницыИзмерения")).МенеджерСсылочногоОбъекта = Новый(СтрЗаменить(Метаданные.НайтиПоТипу(ТипСсылочногоОбъекта).ПолноеИмя, "Объект.", "Менеджер.")).Например, ищем единицу измерения по наименованию из входных данных:
// ЗначениеИзКолонки - это строковое наименование единицы измерения из файла
СсылочныйОбъект = МенеджерСсылочногоОбъекта.НайтиПоНаименованию(ЗначениеИзКолонки);
Если СсылочныйОбъект = Неопределено Тогда
// Если не найдено, создаем новый элемент
НоваяЕдиница = МенеджерСсылочногоОбъекта.СоздатьЭлемент();
НоваяЕдиница.Наименование = ЗначениеИзКолонки;
// ... Заполняем другие реквизиты НовойЕдиницы, если нужно
НоваяЕдиница.Записать();
СсылочныйОбъект = НоваяЕдиница.Ссылка;
КонецЕсли;
НовыйОбъект.ЕдиницаИзмерения = СсылочныйОбъект;
НовыйОбъект.Наименование = ЗначениеИзКолонки;
НовыйОбъект.Записать().Таким образом, комбинируя динамическое получение менеджеров и их методов создания, мы получаем мощный и гибкий механизм для универсальной работы с любыми прикладными объектами в 1С, что идеально подходит для задач типа универсальной загрузки данных, обмена или динамического создания документов и элементов справочников по сложным алгоритмам.
Резюме: Оператор Новый(Тип) сам по себе не является универсальным конструктором для всех типов в 1С. Для прикладных объектов, таких как СправочникОбъект или ДокументОбъект, он возвращает неинициализированные заготовки. Правильный и общепринятый подход заключается в динамическом получении менеджера объекта (используя Новый() для имени менеджера, сформированного через метаданные) и последующем вызове специализированных методов менеджера, таких как СоздатьЭлемент() или СоздатьДокумент(), которые обеспечивают полную инициализацию и готовность объекта к работе.