Как программно определить подсистему, из которой открывается или создается документ в 1С?

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

При разработке конфигураций на платформе 1С:Предприятие 8.3 (Управляемые формы) часто возникает задача адаптации интерфейса и логики заполнения документов под конкретный контекст. Типичный пример: один и тот же документ, например, ПоступлениеТоваровУслуг, включен в две разные подсистемы — «Товары» и «Услуги». Разработчику необходимо сделать так, чтобы при создании документа из подсистемы «Услуги» реквизит ТипТовара автоматически принимал значение Перечисления.ТипТовара.Услуга, а при создании из раздела «Товары» — соответственно, Перечисления.ТипТовара.Товар.

На первый взгляд кажется, что задача решается простым обращением к метаданным в модуле формы. Однако попытки написать код вроде Объект.Ссылка.Метаданные.Подсистемы.Содержит(...) приводят к ошибкам или не дают желаемого результата. В этой статье мы подробно разберем, почему статический подход с метаданными принципиально не работает, проанализируем ограничения платформы и рассмотрим четыре эффективных способа решения этой задачи в соответствии со стандартами разработки 1С.

Почему обращение к метаданным не решает задачу?

Давайте проанализируем, почему код, предложенный на некоторых форумах, не работает:


// Этот код работать не будет так, как ожидает разработчик!
Если Объект.Ссылка.Метаданные.Подсистемы.Содержит(Метаданные.Подсистемы.Услуги) Тогда
    Объект.ТипТовара = Перечисления.ТипТовара.Услуга;
КонецЕсли;

Здесь кроются сразу две принципиальные ошибки:

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

Выяснив причину проблемы, перейдем к рассмотрению правильных архитектурных решений.

Способ №1. Создание параметризованных команд (Классический метод)

Если нам необходимо жестко разделить создание документов по разным разделам интерфейса, правильным решением будет отказ от стандартной команды создания документа в пользу собственных команд.

Разберем этот метод по шагам:

  1. Откроем конфигуратор и найдем наш документ ПоступлениеТоваровУслуг.
  2. В свойствах документа снимем флаг «Использовать стандартные команды». Это скроет стандартные кнопки добавления из интерфейса списков подсистем.
  3. Создадим две новые команды документа (или общие команды): СоздатьПоступлениеТоваров и СоздатьПоступлениеУслуг.
  4. Разместим команду СоздатьПоступлениеТоваров в подсистеме «Товары», а команду СоздатьПоступлениеУслуг — в подсистеме «Услуги».
  5. В модуле команды СоздатьПоступлениеУслуг напишем следующий код, который открывает форму создания нового документа и передает специальный параметр:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("РежимРаботы", "Услуги");
    
    ОткрытьФорму(
        "Документ.ПоступлениеТоваровУслуг.Форма.ФормаДокумента", 
        ПараметрыФормы, 
        ПараметрыВыполненияКоманды.Источник, 
        ПараметрыВыполненияКоманды.Уникальность, 
        ПараметрыВыполненияКоманды.Окно, 
        ПараметрыВыполненияКоманды.НавигационнаяСсылка
    );
КонецПроцедуры

Аналогичный код, но с параметром "Товары", пропишем в модуле команды создания товаров.

Теперь нам остается перехватить переданный параметр в форме документа. Откроем модуль управляемой формы документа и в обработчике ПриСозданииНаСервере пропишем логику заполнения:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Проверяем, что документ новый, а не открывается для просмотра
    Если Объект.Ссылка.Пустая() Тогда
        Если Параметры.Свойство("РежимРаботы") Тогда
            Если Параметры.РежимРаботы = "Услуги" Тогда
                Объект.ТипТовара = ПредопределенноеЗначение("Перечисление.ТипТовара.Услуга");
            ИначеЕсли Параметры.РежимРаботы = "Товары" Тогда
                Объект.ТипТовара = ПредопределенноеЗначение("Перечисление.ТипТовара.Товар");
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Этот способ является абсолютно надежным, поскольку контекст создания явно передается из конкретной команды интерфейса.

Способ №2. Автозаполнение из отборов динамического списка (Рекомендуемый метод 1С)

Если пользователи работают со списками документов в соответствующих разделах, платформа 1С предоставляет изящный стандартный механизм автозаполнения. Он позволяет автоматически инициализировать реквизиты нового объекта на основе установленных отборов в форме списка.

Рассмотрим подробнее, как настроить этот механизм без написания лишнего кода в форме:

  1. В конфигураторе откройте свойства реквизита ТипТовара вашего документа.
  2. Найдите свойство «Заполнять из данных заполнения» и установите его в значение Истина.
  3. В подсистеме «Услуги» настройте форму списка так, чтобы в динамическом списке документов по умолчанию стоял фиксированный отбор: ТипТовара Равно Перечисления.ТипТовара.Услуга.
  4. В подсистеме «Товары» настройте аналогичный отбор со значением Товар.

Теперь при нажатии стандартной кнопки «Создать» из списка документов в разделе «Услуги» платформа автоматически передаст структуру отбора в параметры заполнения нового документа. Чтобы обработать эти данные на уровне бизнес-логики, в модуле объекта документа переопределите обработчик ОбработкаЗаполнения:


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
    // Платформа автоматически заполнит реквизит ТипТовара, 
    // если он взят из отбора списка с флагом "Заполнять из данных заполнения"
    // При необходимости здесь можно дописать дополнительную проверку:
    Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура") Тогда
        Если ДанныеЗаполнения.Свойство("ТипТовара") Тогда
            // Значение уже заполнено платформой, выполняем связанную логику
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

Способ №3. Раздельные формы и КлючНазначенияИспользования

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

В таком случае лучшим решением будет создание двух разных форм документа:

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

Пример кода вызова формы с использованием ключа назначения:


ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("КлючНазначенияИспользования", "РежимУслуги");
ОткрытьФорму("Документ.ПоступлениеТоваровУслуг.Форма.ФормаДокументаУслуги", ПараметрыФормы);

Способ №4. Применение функциональных опций

Если разделение логики на «Товары» и «Услуги» определяется глобальными настройками программы или функциональными ролями пользователей, рекомендуется задействовать механизм Функциональных опций.

Привязка видимости реквизитов формы и команд к функциональным опциям позволяет полностью исключить написание громоздких условных конструкций в коде модулей форм. Платформа самостоятельно управляет отображением элементов интерфейса в зависимости от активных опций системы.

Резюме

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

← На главную