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