Коллеги, довольно часто возникает необходимость гибко настраивать поведение системы в зависимости от конкретных данных, например, от выбранного контрагента. Одним из таких кейсов является динамическое изменение списка документов, доступных для ввода на основании другого документа. Стандартный механизм "Ввода на основании" платформенной кнопки не всегда позволяет достичь требуемой гибкости. В этой статье мы подробно разберем, как реализовать такую функциональность без "костылей", используя возможности платформы 1С:Предприятие 8.3, в частности, Библиотеку стандартных подсистем (БСП) и расширения конфигурации.
Если ваша конфигурация разработана на основе Библиотеки стандартных подсистем (БСП), мы имеем отличную базу для решения этой задачи. БСП предоставляет мощный механизм Подключаемые команды, который активно используется в типовых конфигурациях для динамического формирования списков доступных действий. Рассмотрим, как это работает и как мы можем использовать этот механизм.
Суть механизма:
В типовых конфигурациях, особенно тех, что используют БСП, при активации строки в журналах или списках документов происходит обновление подключаемых команд. Это означает, что система анализирует текущий контекст (например, тип документа, его состояние, или, в нашем случае, реквизиты, связанные с контрагентом) и на основе этого формирует актуальный набор команд, доступных пользователю. В том числе это касается и команд "Ввода на основании".
Как это проявляется на практике?
Возьмем, к примеру, Журнал документов продажи в 1С:ERP Управление предприятием (как было упомянуто в обсуждении на форуме). Мы можем заметить, что состав команд "Ввода на основании" может меняться в зависимости от выбранного документа. Например, для "Заказа клиента" может быть доступна "Реализация товаров и услуг", "Счет-фактура", а для "Отчета о розничных продажах" – только "Приходный кассовый ордер". Это свидетельствует о том, что система динамически определяет, что может быть введено на основании текущего документа.
Что нам нужно изучить:
Реализация через БСП (общий подход):
Если мы хотим влиять на список "Ввода на основании" в рамках БСП, нам, возможно, придется модифицировать или дополнять код, который генерирует эти команды. Это может включать:
Этот подход требует хорошего знания структуры БСП и готовности к внесению изменений, которые могут повлиять на стандартную логику. Однако, он позволяет интегрироваться с типовыми механизмами и избежать "костылей".
Платформенная кнопка "Создать на основании", расположенная непосредственно на форме документа, не предоставляет гибких возможностей для динамического изменения списка доступных документов. Однако, расширения конфигурации приходят нам на помощь, позволяя полностью переопределить или дополнить эту функциональность. Этот способ считается одним из наиболее чистых и предпочтительных, так как он не затрагивает основную конфигурацию и не препятствует ее обновлению.
Основные шаги:
Разберем по шагам, как мы можем реализовать собственную логику "Ввода на основании" с учетом контрагента, используя расширения.
Первым делом, мы добавим новую общую команду в наше расширение. Эта команда будет служить точкой входа для нашей логики.
ВводНаОснованииПоКонтрагенту.ДокументСсылка.СчетНаОплатуКлиенту. Это позволит платформе автоматически передавать ссылку на текущий документ в нашу команду.В модуле созданной нами общей команды мы опишем процедуру ОбработкаКоманды. Именно здесь будет содержаться вся логика по определению типа нового документа и его созданию, с учетом нашего контрагента.
Давайте посмотрим на пример кода:
// В модуле общей команды "ВводНаОснованииПоКонтрагенту"
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды) Экспорт
// ПараметрКоманды - это ссылка на документ-основание,
// тип которого мы указали в свойствах команды.
Если ТипЗнч(ПараметрКоманды) = Тип("ДокументСсылка.СчетНаОплатуКлиенту") Тогда
// Получаем ссылку на документ-основание
СсылкаОснование = ПараметрКоманды;
// Получаем объект документа-основания для доступа к его реквизитам
// Это позволит нам проанализировать данные контрагента
ОбъектОснование = СсылкаОснование.ПолучитьОбъект();
// **Основная логика: определение типа документа на основании контрагента**
// Предположим, что мы хотим ввести разные документы
// для разных контрагентов или условий.
Если ОбъектОснование.Контрагент.Наименование = "ОсобыйКонтрагент" Тогда
// Для "ОсобогоКонтрагента" вводим "Реализацию товаров и услуг"
НовыйДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Сообщить("Создается Реализация товаров и услуг для ОсобогоКонтрагента.");
ИначеЕсли ОбъектОснование.Контрагент.ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель Тогда
// Для обычных покупателей вводим "Заказ покупателя"
НовыйДокументОбъект = Документы.ЗаказПокупателя.СоздатьДокумент();
Сообщить("Создается Заказ покупателя для обычного контрагента.");
Иначе
// В остальных случаях, например, "Счет-фактуру выданный"
НовыйДокументОбъект = Документы.СчетФактураВыданный.СоздатьДокумент();
Сообщить("Создается Счет-фактура выданный для прочих контрагентов.");
КонецЕсли;
// **Заполнение нового документа на основании**
// Метод Заполнить() вызовет обработчик ОбработкаЗаполнения в новом документе.
НовыйДокументОбъект.Заполнить(СсылкаОснование);
// **Открытие формы нового документа**
ФормаНовогоДокумента = НовыйДокументОбъект.ПолучитьФорму("ФормаДокумента");
// Если требуется передать дополнительный параметр-основание, можно использовать:
// ФормаНовогоДокумента.ПараметрОснование = СсылкаОснование; // Это свойство расширения формы
ФормаНовогоДокумента.Открыть();
Иначе
Сообщить("Команда не применима для данного типа документа-основания.", СтатусСообщения.Внимание);
КонецЕсли;
КонецПроцедуры
Важный момент: Кнопка или Команда?
Как было упомянуто на форуме, для нас, как для разработчиков, команда — это запрограммированное действие, а кнопка — это визуальный элемент на форме, который запускает эту команду. Когда мы говорим о "своей кнопке" в контексте "Ввода на основании", мы подразумеваем свою команду, которая отображается как элемент в стандартном меню "Создать на основании". Платформа сама управляет отображением этих команд как пунктов меню или кнопок.
Для того чтобы вновь созданный документ корректно заполнился данными из документа-основания, мы должны реализовать или доработать процедуру ОбработкаЗаполнения в модуле объекта нового документа. Эта процедура вызывается автоматически методом Заполнить(), который мы использовали в нашей команде.
Пример процедуры ОбработкаЗаполнения:
// В модуле объекта документа "РеализацияТоваровУслуг" (или другого нового документа)
Процедура ОбработкаЗаполнения(Основание)
// Основание - это ссылка на документ, на основании которого создается текущий.
Если ТипЗнч(Основание) = Тип("ДокументСсылка.СчетНаОплатуКлиенту") Тогда
// Заполняем основные реквизиты нового документа из основания
ЭтотОбъект.Контрагент = Основание.Контрагент;
ЭтотОбъект.Организация = Основание.Организация;
ЭтотОбъект.ДоговорКонтрагента = Основание.ДоговорКонтрагента;
ЭтотОбъект.ВалютаДокумента = Основание.ВалютаДокумента;
ЭтотОбъект.Ответственный = Пользователи.ТекущийПользователь();
// Заполнение табличных частей, например, товары
Для Каждого СтрокаОснования Из Основание.Товары Цикл
НоваяСтрока = ЭтотОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = СтрокаОснования.Номенклатура;
НоваяСтрока.Количество = СтрокаОснования.Количество;
НоваяСтрока.Цена = СтрокаОснования.Цена;
НоваяСтрока.Сумма = СтрокаОснования.Сумма;
// Копируем другие необходимые реквизиты строк
НоваяСтрока.СтавкаНДС = СтрокаОснования.СтавкаНДС;
НоваяСтрока.СуммаНДС = СтрокаОснования.СуммаНДС;
КонецЦикла;
// Если документ-основание имеет дополнительные табличные части,
// которые необходимо перенести, добавляем аналогичную логику.
// Возможно, потребуется пересчет сумм или других показателей
// после заполнения табличных частей
ЭтотОбъект.РассчитатьСуммуДокумента();
ИначеЕсли ТипЗнч(Основание) = Тип("ДокументСсылка.ЗаказПокупателя") Тогда
// ... логика заполнения, если основание - ЗаказПокупателя
КонецЕсли;
// В БСП часто используются вспомогательные функции для заполнения
// или подключаемые обработчики, например, из подсистемы "ОбменДанными".
// Рекомендуется изучить, как это реализовано в типовых конфигурациях.
КонецПроцедуры
Интеграция с БСП (если применимо):
Если ваша конфигурация использует БСП (например, версии 3.1.10 и выше), механизм вывода команд в интерфейс может быть дополнительно интегрирован с подсистемой "Подключаемые команды". Это позволяет централизованно управлять видимостью и доступностью команд. При создании собственной команды через расширение, мы можем зарегистрировать ее в этой подсистеме, если хотим использовать ее расширенные возможности (например, программное управление доступностью в зависимости от прав или других условий).
Хотя для меню "Ввод на основании" достаточно создать общую команду с правильной группой, иногда возникает потребность динамически добавлять кнопки на форму в других местах. Это также возможно программно. Мы можем создать элемент формы типа "Кнопка" и связать его с нашей стандартной или собственной командой.
// Пример добавления кнопки на форму (обычно в событии ПриСозданииНаСервере)
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// ... стандартный код формы
// Создаем новый элемент типа "Кнопка"
НоваяКнопка = Элементы.Добавить("МояДинамическаяКнопка", Тип("КнопкаФормы"), Элементы.КоманднаяПанель);
НоваяКнопка.Заголовок = "Специальный Ввод";
НоваяКнопка.Вид = ВидКнопкиФормы.ОбычнаяКнопка;
// Если у нас есть команда формы, связываем кнопку с ней
// Например, если мы добавили команду формы "МояКомандаФормы"
НоваяКнопка.ИмяКоманды = "МояКомандаФормы";
// Можно также создать команду программно и связать ее
Если Объект.Контрагент.Наименование = "ОсобыйКонтрагент" Тогда
НоваяКнопка.Видимость = Истина;
Иначе
НоваяКнопка.Видимость = Ложь;
КонецЕсли;
// ...
КонецПроцедуры
Однако, для реализации логики "Ввод на основании" достаточно шагов 1-3, используя механизм общих команд с правильной группой.
Если по каким-то причинам нам не подходит использование общих команд или БСП, мы можем реализовать ввод на основании полностью программно в любом удобном месте (например, в обработке или в событии формы). Этот подход дает максимальный контроль, но требует больше ручного кодирования.
Основные этапы:
СоздатьДокумент().
НовыйДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Заполнить(), передавая ему ссылку на документ-основание. Это инициирует выполнение процедуры ОбработкаЗаполнения в модуле нового объекта.
// Предположим, что СсылкаНаОснование - это документ-основание
НовыйДокументОбъект.Заполнить(СсылкаНаОснование);
Форма = НовыйДокументОбъект.ПолучитьФорму("ФормаДокумента");
// Если форма документа имеет свойство расширения формы "ПараметрОснование",
// можно его установить для передачи дополнительной информации
// Форма.ПараметрОснование = СсылкаНаОснование;
Форма.Открыть();
Заполнить() или ОбработкаЗаполнения не предоставляют нужной гибкости, мы можем использовать метод НаполнитьЗначенияСвойств для выборочного копирования реквизитов из базового документа в новый.
// Создаем новый документ
НовыйДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// Определяем, какие свойства хотим скопировать
МассивСвойствДляКопирования = Новый Массив();
МассивСвойствДляКопирования.Добавить("Контрагент");
МассивСвойствДляКопирования.Добавить("Организация");
МассивСвойствДляКопирования.Добавить("ДоговорКонтрагента");
// Копируем свойства из объекта-основания в новый объект
// Предположим, ОбъектОснования - это уже полученный объект документа-основания
НаполнитьЗначенияСвойств(НовыйДокументОбъект, ОбъектОснования, МассивСвойствДляКопирования);
// ... дальнейшее заполнение табличных частей и открытие формы
Этот подход полезен, когда логика "Ввода на основании" сильно специфична и не укладывается в стандартные механизмы или когда мы хотим предоставить совершенно другой пользовательский интерфейс для этого действия.
Хотя основной вопрос касается команд "Ввод на основании" для конкретного документа, стоит упомянуть возможность программного изменения состава команд, доступных в динамических списках. Если мы работаем с формой, содержащей динамический список (например, список справочника или документов), мы можем влиять на его командную панель.
Механизм:
У динамического списка есть свойство "Состав команд", которое можно изменить программно. Это может быть полезно, если наша задача состоит не в добавлении новых типов документов для ввода на основании, а в сокрытии или отображении уже существующих команд, которые относятся к элементам списка.
Пример использования:
// В обработчике события, например, ПриСозданииНаСервере или ПриИзменении
// элемента формы (например, поля выбора контрагента)
&НаСервере
Процедура КонтрагентПриИзмененииНаСервере()
// Предположим, что у нас есть динамический список с именем "СписокДокументов"
// и мы хотим управлять видимостью команд в зависимости от выбранного контрагента.
Если Объект.Контрагент.Наименование = "ОсобыйКонтрагент" Тогда
// Делаем видимой команду "СпециальныйОтчет" для этого контрагента
Элементы.СписокДокументов.КоманднаяПанель.Элементы.СпециальныйОтчет.Видимость = Истина;
// Скрываем другую команду
Элементы.СписокДокументов.КоманднаяПанель.Элементы.СтандартнаяКоманда.Видимость = Ложь;
Иначе
// Обратная логика для других контрагентов
Элементы.СписокДокументов.КоманднаяПанель.Элементы.СпециальныйОтчет.Видимость = Ложь;
Элементы.СписокДокументов.КоманднаяПанель.Элементы.СтандартнаяКоманда.Видимость = Истина;
КонецЕсли;
// Также можно влиять на параметры динамического списка,
// если список должен отображать разные данные для разных контрагентов
// Например, если СписокМатериалов - это элемент формы типа ДинамическийСписок:
// Элементы.СписокМатериалов.Параметры.УстановитьЗначениеПараметра("Контрагент", Объект.Контрагент);
// Элементы.СписокМатериалов.Обновить();
КонецПроцедуры
Важно учитывать особенности реализации команд для форм списков, особенно при группировке данных. Этот метод больше подходит для управления общей командной панелью списка, а не непосредственно для формирования списка "Ввода на основании" из одного конкретного документа.
Мы видим, что существует несколько действенных способов решить задачу динамического изменения списка документов для ввода на основании в зависимости от контрагента. Выбор конкретного подхода будет зависеть от архитектуры вашей конфигурации (используется ли БСП), сложности требуемой логики и ваших предпочтений относительно чистоты решения (расширения vs. прямое изменение конфигурации). В любом случае, современные инструменты 1С:Предприятие 8.3 предоставляют все необходимое для гибкой и мощной настройки.