Как обратиться к Планам видов характеристик из общего модуля в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При разработке в среде «1С:Предприятие 8.3» программисты часто сталкиваются с ошибкой компиляции вида «Переменная не определена (ПланыВидовХарактеристик)» (или аналогичной для справочников, документов и других менеджеров объектов) при попытке обратиться к метаданным из общего модуля. Давайте подробно разберем, почему возникает эта ошибка, как устроена контекстная компиляция в 1С, и как правильно решать подобные задачи с использованием современных стандартов разработки и Библиотеки стандартных подсистем (БСП).

Выясняем причину: особенности компиляции общих модулей

Первым делом проанализируем, почему платформа выдает ошибку на стандартную строку кода, которая отлично работает в модуле менеджера или модуле объекта. Причина кроется в свойствах общего модуля, в котором пишется код. Если в свойствах общего модуля одновременно установлены флаги «Клиент (управляемое приложение)» и «Сервер», компилятор 1С пытается проверить синтаксис модуля для обоих контекстов.

На тонком клиенте (в режиме управляемого приложения) глобальный контекст сильно ограничен из-за соображений безопасности и производительности. Платформа не позволяет напрямую обращаться к менеджерам баз данных (таким как ПланыВидовХарактеристик, Справочники, Документы, РегистрыСведений и т.д.) с клиента. Клиентская часть просто «не знает» об их существовании, что и приводит к ошибке «Переменная не определена» на этапе компиляции клиентской части общего модуля.

Важно помнить, что директивы компиляции вида &НаСервере или &НаКлиенте в общих модулях не используются — они предназначены исключительно для модулей форм и команд. В общих модулях управление контекстом выполняется с помощью инструкций препроцессора.

Решение 1. Разграничение контекста с помощью препроцессора

Если общий модуль обязательно должен быть доступен и на клиенте, и на сервере, нам необходимо явно указать компилятору, какие участки кода должны исполняться только на сервере. Для этого мы оборачиваем наше обращение к серверным объектам в специальные инструкции препроцессора.

Давайте рассмотрим, как должен выглядеть код в этом случае:


#Если Сервер Или ТолстыйКлиентОбычноеПриложение Тогда

Попытка
    КальяннаяСмесь = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "КальяннаяСмесь");
    Если Не КальяннаяСмесь.Пустая() Тогда
        НайденнаяСтрока = СтрокаТЧ.Номенклатура.ДополнительныеРеквизиты.Найти(КальяннаяСмесь, "Свойство");
        Если НайденнаяСтрока <> Неопределено Тогда
            Если НайденнаяСтрока.Значение = Истина Тогда
                СтрокаПозицииЧека.Наименование = "Кальянная смесь";
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
Исключение
    // Обработка исключения при необходимости
КонецПопытки;

#КонецЕсли

При такой конструкции компилятор пропустит данный кусок кода при компиляции под тонкий клиент, и ошибка исчезнет. Однако прямое обращение к метаданным через НайтиПоРеквизиту имеет ряд серьезных недостатков, о которых мы поговорим далее.

Решение 2. Использование стандартных механизмов БСП (Правильный подход)

В современных конфигурациях, таких как «Управление торговлей 11.4», «ERP Управление предприятием» или «Комплексная автоматизация», используется Библиотека стандартных подсистем (БСП). Разработчики БСП уже предусмотрели готовые и оптимизированные механизмы для работы со свойствами и дополнительными реквизитами.

Прямой поиск элемента плана видов характеристик по внутреннему имени (реквизиту Имя) считается плохим тоном. Имя дополнительного реквизита, созданного пользователем в пользовательском режиме, генерируется автоматически (например, Номенклатура_Общие_Цвет) и может не совпасть с жестко прописанной строкой в коде. Кроме того, поиск по реквизиту чувствителен к регистру букв.

Для безопасного и быстрого получения значений дополнительных реквизитов рекомендуется использовать экспортные функции общего модуля УправлениеСвойствами. Посмотрим на практический пример использования БСП:


#Если Сервер Или ТолстыйКлиентОбычноеПриложение Тогда

// Используем кэшированные и оптимизированные механизмы БСП
Если УправлениеСвойствами.ЗначениеСвойства(СтрокаТЧ.Номенклатура, "КальяннаяСмесь") = Истина Тогда
    СтрокаПозицииЧека.Наименование = "Кальянная смесь";
КонецЕсли;

#КонецЕсли

Функция УправлениеСвойствами.ЗначениеСвойства самостоятельно найдет нужное свойство, определит его значение для конкретного объекта номенклатуры и вернет результат. Это избавляет разработчика от необходимости писать громоздкие конструкции с проверками на пустые ссылки и Неопределено.

Решение 3. Оптимизация производительности с помощью запросов

Если код, представленный выше, выполняется внутри цикла (например, при обходе строк табличной части документа продаж или чека ККМ), то использование методов НайтиПоРеквизиту или даже одиночных вызовов функций БСП приведет к катастрофическому падению производительности. Это классическая проблема «N+1 запрос к базе данных».

Проанализируем ситуацию: если в чеке 50 строк, система выполнит 50 отдельных микрозапросов к СУБД для поиска свойства по каждой номенклатуре. Правильным и профессиональным решением будет получение всех необходимых данных одним запросом перед началом цикла.

Рассмотрим пример комплексного решения этой задачи:


// 1. Собираем массив всей номенклатуры из табличной части для пакетной обработки
МассивНоменклатуры = Новый Массив;
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    МассивНоменклатуры.Добавить(СтрокаТЧ.Номенклатура);
КонецЦикла;

// Исключаем дубликаты для оптимизации запроса
ОбщегоНазначенияКлиентСервер.СвернутьМассив(МассивНоменклатуры);

// 2. Одним запросом выбираем значения нужного доп. реквизита для всего массива номенклатуры
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	НоменклатураДополнительныеРеквизиты.Ссылка КАК Номенклатура,
    |	НоменклатураДополнительныеРеквизиты.Значение КАК Значение
    |ИЗ
    |	Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
    |ГДЕ
    |	НоменклатураДополнительныеРеквизиты.Ссылка В(&МассивНоменклатуры)
    |	И НоменклатураДополнительныеРеквизиты.Свойство.Имя = &ИмяСвойства";

Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
// Задаем имя свойства, настроенное в БСП
Запрос.УстановитьПараметр("ИмяСвойства", "КальяннаяСмесь_e7b9bc90"); 

Выборка = Запрос.Выполнить().Выбрать();

// 3. Заполняем соответствие для быстрого поиска в оперативной памяти
СоответствиеСвойств = Новый Соответствие;
Пока Выборка.Следующий() Цикл
    СоответствиеСвойств.Вставить(Выборка.Номенклатура, Выборка.Значение);
КонецЦикла;

// 4. В цикле быстро сопоставляем данные без обращений к базе данных
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    ЗначениеРеквизита = СоответствиеСвойств.Получить(СтрокаТЧ.Номенклатура);
    Если ЗначениеРеквизита = Истина Тогда
        // Здесь выполняем логику изменения наименования в позиции чека
        // СтрокаПозицииЧека.Наименование = "Кальянная смесь";
    КонецЕсли;
КонецЦикла;

Такой подход гарантирует минимальную нагрузку на сервер СУБД, повышает отказоустойчивость системы и обеспечивает мгновенный отклик интерфейса при оформлении документов продаж.

Резюме и рекомендации

Для успешного написания чистого и быстрого кода в общих модулях «1С:Предприятия» придерживайтесь следующих правил:

  1. Всегда анализируйте свойства общего модуля. Если взведен флаг «Клиент», защищайте серверный код инструкциями препроцессора #Если Сервер Тогда.
  2. Избегайте жесткого кодирования (hardcode) строковых имен свойств и прямого использования метода НайтиПоРеквизиту. По возможности используйте предопределенные элементы.
  3. Активно задействуйте программный интерфейс БСП (модуль УправлениеСвойствами) — это экономит время разработки и гарантирует совместимость при обновлениях конфигурации.
  4. Никогда не выполняйте запросы и поиск элементов баз данных внутри циклов. Сначала соберите массив уникальных ссылок, сделайте один пакетный запрос, сохраните результат в Соответствие и только потом обрабатывайте коллекцию.
← На главную