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

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

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

Подход 1: Использование Библиотеки стандартных подсистем (БСП) для динамического обновления команд

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

Суть механизма:

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

Как это проявляется на практике?

Возьмем, к примеру, Журнал документов продажи в 1С:ERP Управление предприятием (как было упомянуто в обсуждении на форуме). Мы можем заметить, что состав команд "Ввода на основании" может меняться в зависимости от выбранного документа. Например, для "Заказа клиента" может быть доступна "Реализация товаров и услуг", "Счет-фактура", а для "Отчета о розничных продажах" – только "Приходный кассовый ордер". Это свидетельствует о том, что система динамически определяет, что может быть введено на основании текущего документа.

Что нам нужно изучить:

  1. Подсистема "Подключаемые команды" в БСП: Мы должны погрузиться в изучение этой подсистемы. Она отвечает за регистрацию команд, определение их видимости, доступности и обработчиков. Основной принцип заключается в том, что команды не жестко прописаны в форме, а "подключаются" к ней в зависимости от контекста.
  2. Алгоритмы обновления команд: В модулях форм журналов или списков документов, использующих БСП, мы найдем процедуры, которые вызывают обновление подключаемых команд при изменении активной строки. Именно в этих процедурах или связанных с ними общих модулях мы можем найти логику, которую нужно будет адаптировать или дополнить.

Реализация через БСП (общий подход):

Если мы хотим влиять на список "Ввода на основании" в рамках БСП, нам, возможно, придется модифицировать или дополнять код, который генерирует эти команды. Это может включать:

  1. Регистрация собственных команд: Мы можем добавить свои команды "Ввода на основании" через механизм БСП, указав для них условия видимости и доступности.
  2. Изменение существующих обработчиков: Мы можем найти общие модули или обработчики, которые отвечают за формирование стандартных команд "Ввода на основании" и внести в них корректировки, чтобы они учитывали реквизит "Контрагент" документа-основания.

Этот подход требует хорошего знания структуры БСП и готовности к внесению изменений, которые могут повлиять на стандартную логику. Однако, он позволяет интегрироваться с типовыми механизмами и избежать "костылей".

Подход 2: Использование расширений конфигурации и своих команд

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

Основные шаги:

Разберем по шагам, как мы можем реализовать собственную логику "Ввода на основании" с учетом контрагента, используя расширения.

1. Создание собственной общей команды в расширении

Первым делом, мы добавим новую общую команду в наше расширение. Эта команда будет служить точкой входа для нашей логики.

  1. Добавление общей команды: В конфигураторе, в дереве объектов расширения, в ветке "Общие" → "Общие команды", создадим новую команду. Назовем ее, например, ВводНаОснованииПоКонтрагенту.
  2. Настройка свойств команды:
    • Группа: Это очень важный шаг. Мы должны указать группу "Командная панель формы. Создать на основании". Это позволит нашей команде появиться в стандартном выпадающем меню "Создать на основании" на форме документа.
    • Тип параметра команды: Укажем тип документа, на основании которого предполагается вводить новые документы. Например, если мы хотим работать со "Счетом на оплату клиенту", то укажем ДокументСсылка.СчетНаОплатуКлиенту. Это позволит платформе автоматически передавать ссылку на текущий документ в нашу команду.
    • Использование: Отметим "Вызов серверного метода" или "Вызов клиент-серверного метода" в зависимости от нашей логики. Чаще всего подойдет клиент-серверный для интерактивных действий.

2. Реализация обработчика команды

В модуле созданной нами общей команды мы опишем процедуру ОбработкаКоманды. Именно здесь будет содержаться вся логика по определению типа нового документа и его созданию, с учетом нашего контрагента.

Давайте посмотрим на пример кода:


// В модуле общей команды "ВводНаОснованииПоКонтрагенту"

Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды) Экспорт
    // ПараметрКоманды - это ссылка на документ-основание,
    // тип которого мы указали в свойствах команды.

    Если ТипЗнч(ПараметрКоманды) = Тип("ДокументСсылка.СчетНаОплатуКлиенту") Тогда
        // Получаем ссылку на документ-основание
        СсылкаОснование = ПараметрКоманды;

        // Получаем объект документа-основания для доступа к его реквизитам
        // Это позволит нам проанализировать данные контрагента
        ОбъектОснование = СсылкаОснование.ПолучитьОбъект();

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

        // **Заполнение нового документа на основании**
        // Метод Заполнить() вызовет обработчик ОбработкаЗаполнения в новом документе.
        НовыйДокументОбъект.Заполнить(СсылкаОснование);

        // **Открытие формы нового документа**
        ФормаНовогоДокумента = НовыйДокументОбъект.ПолучитьФорму("ФормаДокумента");
        // Если требуется передать дополнительный параметр-основание, можно использовать:
        // ФормаНовогоДокумента.ПараметрОснование = СсылкаОснование; // Это свойство расширения формы
        ФормаНовогоДокумента.Открыть();

    Иначе
        Сообщить("Команда не применима для данного типа документа-основания.", СтатусСообщения.Внимание);
    КонецЕсли;
КонецПроцедуры

Важный момент: Кнопка или Команда?

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

3. Реализация или доработка процедуры ОбработкаЗаполнения

Для того чтобы вновь созданный документ корректно заполнился данными из документа-основания, мы должны реализовать или доработать процедуру ОбработкаЗаполнения в модуле объекта нового документа. Эта процедура вызывается автоматически методом Заполнить(), который мы использовали в нашей команде.

Пример процедуры ОбработкаЗаполнения:


// В модуле объекта документа "РеализацияТоваровУслуг" (или другого нового документа)

Процедура ОбработкаЗаполнения(Основание)
    // Основание - это ссылка на документ, на основании которого создается текущий.

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

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

        // Если документ-основание имеет дополнительные табличные части,
        // которые необходимо перенести, добавляем аналогичную логику.

        // Возможно, потребуется пересчет сумм или других показателей
        // после заполнения табличных частей
        ЭтотОбъект.РассчитатьСуммуДокумента();

    ИначеЕсли ТипЗнч(Основание) = Тип("ДокументСсылка.ЗаказПокупателя") Тогда
        // ... логика заполнения, если основание - ЗаказПокупателя
    КонецЕсли;

    // В БСП часто используются вспомогательные функции для заполнения
    // или подключаемые обработчики, например, из подсистемы "ОбменДанными".
    // Рекомендуется изучить, как это реализовано в типовых конфигурациях.
КонецПроцедуры

Интеграция с БСП (если применимо):

Если ваша конфигурация использует БСП (например, версии 3.1.10 и выше), механизм вывода команд в интерфейс может быть дополнительно интегрирован с подсистемой "Подключаемые команды". Это позволяет централизованно управлять видимостью и доступностью команд. При создании собственной команды через расширение, мы можем зарегистрировать ее в этой подсистеме, если хотим использовать ее расширенные возможности (например, программное управление доступностью в зависимости от прав или других условий).

4. Программное создание кнопки (дополнительно)

Хотя для меню "Ввод на основании" достаточно создать общую команду с правильной группой, иногда возникает потребность динамически добавлять кнопки на форму в других местах. Это также возможно программно. Мы можем создать элемент формы типа "Кнопка" и связать его с нашей стандартной или собственной командой.


// Пример добавления кнопки на форму (обычно в событии ПриСозданииНаСервере)

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // ... стандартный код формы

    // Создаем новый элемент типа "Кнопка"
    НоваяКнопка = Элементы.Добавить("МояДинамическаяКнопка", Тип("КнопкаФормы"), Элементы.КоманднаяПанель);
    НоваяКнопка.Заголовок = "Специальный Ввод";
    НоваяКнопка.Вид = ВидКнопкиФормы.ОбычнаяКнопка;

    // Если у нас есть команда формы, связываем кнопку с ней
    // Например, если мы добавили команду формы "МояКомандаФормы"
    НоваяКнопка.ИмяКоманды = "МояКомандаФормы";

    // Можно также создать команду программно и связать ее
    Если Объект.Контрагент.Наименование = "ОсобыйКонтрагент" Тогда
        НоваяКнопка.Видимость = Истина;
    Иначе
        НоваяКнопка.Видимость = Ложь;
    КонецЕсли;

    // ...
КонецПроцедуры

Однако, для реализации логики "Ввод на основании" достаточно шагов 1-3, используя механизм общих команд с правильной группой.

Подход 3: Программная реализация ввода на основании без явных команд

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

Основные этапы:

  1. Создание нового объекта документа: Мы начинаем с создания нового экземпляра нужного нам типа документа, используя метод СоздатьДокумент().
    
    НовыйДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
    
  2. Заполнение на основании: Вызываем у созданного объекта метод Заполнить(), передавая ему ссылку на документ-основание. Это инициирует выполнение процедуры ОбработкаЗаполнения в модуле нового объекта.
    
    // Предположим, что СсылкаНаОснование - это документ-основание
    НовыйДокументОбъект.Заполнить(СсылкаНаОснование);
    
  3. Получение и открытие формы: После заполнения объекта, мы получаем его форму и открываем ее для пользователя.
    
    Форма = НовыйДокументОбъект.ПолучитьФорму("ФормаДокумента");
    // Если форма документа имеет свойство расширения формы "ПараметрОснование",
    // можно его установить для передачи дополнительной информации
    // Форма.ПараметрОснование = СсылкаНаОснование;
    Форма.Открыть();
    
  4. Копирование свойств (альтернатива/дополнение к Заполнить): В некоторых случаях, если метод Заполнить() или ОбработкаЗаполнения не предоставляют нужной гибкости, мы можем использовать метод НаполнитьЗначенияСвойств для выборочного копирования реквизитов из базового документа в новый.
    
    // Создаем новый документ
    НовыйДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
    
    // Определяем, какие свойства хотим скопировать
    МассивСвойствДляКопирования = Новый Массив();
    МассивСвойствДляКопирования.Добавить("Контрагент");
    МассивСвойствДляКопирования.Добавить("Организация");
    МассивСвойствДляКопирования.Добавить("ДоговорКонтрагента");
    
    // Копируем свойства из объекта-основания в новый объект
    // Предположим, ОбъектОснования - это уже полученный объект документа-основания
    НаполнитьЗначенияСвойств(НовыйДокументОбъект, ОбъектОснования, МассивСвойствДляКопирования);
    
    // ... дальнейшее заполнение табличных частей и открытие формы
    

Этот подход полезен, когда логика "Ввода на основании" сильно специфична и не укладывается в стандартные механизмы или когда мы хотим предоставить совершенно другой пользовательский интерфейс для этого действия.

Подход 4: Изменение списка команд динамического списка

Хотя основной вопрос касается команд "Ввод на основании" для конкретного документа, стоит упомянуть возможность программного изменения состава команд, доступных в динамических списках. Если мы работаем с формой, содержащей динамический список (например, список справочника или документов), мы можем влиять на его командную панель.

Механизм:

У динамического списка есть свойство "Состав команд", которое можно изменить программно. Это может быть полезно, если наша задача состоит не в добавлении новых типов документов для ввода на основании, а в сокрытии или отображении уже существующих команд, которые относятся к элементам списка.

Пример использования:


// В обработчике события, например, ПриСозданииНаСервере или ПриИзменении
// элемента формы (например, поля выбора контрагента)

&НаСервере
Процедура КонтрагентПриИзмененииНаСервере()
    // Предположим, что у нас есть динамический список с именем "СписокДокументов"
    // и мы хотим управлять видимостью команд в зависимости от выбранного контрагента.

    Если Объект.Контрагент.Наименование = "ОсобыйКонтрагент" Тогда
        // Делаем видимой команду "СпециальныйОтчет" для этого контрагента
        Элементы.СписокДокументов.КоманднаяПанель.Элементы.СпециальныйОтчет.Видимость = Истина;
        // Скрываем другую команду
        Элементы.СписокДокументов.КоманднаяПанель.Элементы.СтандартнаяКоманда.Видимость = Ложь;
    Иначе
        // Обратная логика для других контрагентов
        Элементы.СписокДокументов.КоманднаяПанель.Элементы.СпециальныйОтчет.Видимость = Ложь;
        Элементы.СписокДокументов.КоманднаяПанель.Элементы.СтандартнаяКоманда.Видимость = Истина;
    КонецЕсли;

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

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

Общая информация и рекомендации по работе с командами и расширениями

  1. Команда и Кнопка: Мы уже обсудили, что команда — это логика действия, а кнопка — это элемент интерфейса для ее запуска. Помните об этом различии при проектировании.
  2. Определение своих команд: Свои команды можно определять не только как общие, но и как команды формы на закладке "Команды" соответствующей формы. Это дает больший контроль над контекстом выполнения.
  3. Расширения — наш лучший друг: Использование расширений конфигурации позволяет нам вносить изменения в функционал без снятия конфигурации с поддержки. Это критически важно для сохранения возможности обновляться на новые версии типовых решений.
  4. Особенности расширений для "Ввода на основании": При работе с расширениями для объектов, которые мы добавляем или модифицируем, нет типовой возможности указать, что они вводятся на основании или являются основанием для других документов через свойства метаданных в расширении. Именно поэтому мы вынуждены использовать объект расширения конфигурации "Команда" для реализации логики "Ввода на основании" программно.
  5. Динамическое обновление в 1С: Платформа 1С предоставляет обширные возможности для динамического изменения поведения системы. Мы можем изменять модули, формы, команды, макеты справочников и документов, отчеты, обработки и другие объекты, используя различные механизмы, включая функциональные опции, параметры сеанса и, конечно, программный код.
  6. Поиск информации: Как справедливо было замечено на форуме, многие аспекты программной работы с формами и командами уже обсуждались. Мы всегда можем обратиться к синтакс-помощнику, а также использовать поиск по таким запросам, как "программное создание форм", "управление командами формы", "общие команды 1С", "расширения конфигурации команды".

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

← На главную