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