Как обойти обязательное проведение документа-основания при создании связанных объектов через БСП

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

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

Для решения подобных задач в Библиотеке стандартных подсистем (БСП) предусмотрен удобный механизм — внешние дополнительные обработки с видом «Создание связанных объектов». Однако здесь разработчиков подстерегает стандартное поведение платформы и БСП: при попытке вызвать команду создания связанного объекта для незаписанного или непроведенного документа система выдает предупреждение: "Для создания на основании требуется провести документ. Выполнить проведение и продолжить?".

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

Анализ проблемы: почему БСП требует проведение?

Для начала проанализируем, как выглядит типичная регистрация внешней обработки создания связанных объектов в модуле объекта. Выясним, какие параметры передаются системе. Рассмотрим стандартную функцию СведенияОВнешнейОбработке:


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
    ПараметрыРегистрации.Информация = НСтр("ru = 'Создание документа ""Передача товаров между организациями"".'");
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиСозданиеСвязанныхОбъектов();
    ПараметрыРегистрации.Версия = "1.0";
    ПараметрыРегистрации.БезопасныйРежим = Ложь;
    ПараметрыРегистрации.Назначение.Добавить(Метаданные.Документы.РеализацияТоваровУслуг.ПолноеИмя());
    
    Команда = ПараметрыРегистрации.Команды.Добавить();
    Команда.Представление = НСтр("ru = 'Сформировать передачу товара между организациями.'");
    Команда.Идентификатор = "ИТМ_СозданиеПередачиТоваровМеждуОрганизациями";
    Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    Команда.ПоказыватьОповещение = Истина;
    
    Возврат ПараметрыРегистрации;
КонецФункции

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


Если (ПолучитьФункциональнуюОпцию("ИспользоватьДополнительныеОбработкиСозданияСвязанныхОбъектов", 
        Новый Структура("ДополнительныеОтчетыИОбработкиОбъектНазначения,ДополнительныеОтчетыИОбработкиТипФормы", 
        Справочники.ИдентификаторыОбъектовМетаданных.НайтиПоРеквизиту("ПолноеИмя",МассивПолей[0]+"."+МассивПолей[1]), МассивПолей[3]))
    Или ПолучитьФункциональнуюОпцию("ИспользоватьДополнительныеОбработкиСозданияСвязанныхОбъектов", 
        Новый Структура("ДополнительныеОтчетыИОбработкиОбъектНазначения,ДополнительныеОтчетыИОбработкиТипФормы", 
        Справочники.ИдентификаторыОбъектовМетаданных.НайтиПоРеквизиту("ПолноеИмя",МассивПолей[0]+"."+МассивПолей[1]), 
        ?(СтрНайти(МассивПолей[3], "ФормаСписка") <> 0,ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыСписка(),
        ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыОбъекта())))) 
    И СтрНайти(ИмяФормы,"ЖурналДокументов") = 0 Тогда

    КомандаСозданияНаОсновании = КомандыСоздатьНаОсновании.Добавить();
    КомандаСозданияНаОсновании.Обработчик = "ВводНаОснованииУТКлиент.СозданиеСвязанныхОбъектов";
    КомандаСозданияНаОсновании.Идентификатор = "СозданиеСвязанныхОбъектов";
    КомандаСозданияНаОсновании.Представление = НСтр("ru = 'Создание связанных объектов...'");
    КомандаСозданияНаОсновании.ПроверкаПроведенияПередСозданиемНаОсновании = Истина;
    КомандаСозданияНаОсновании.Порядок = 100;
КонецЕсли;

Обратите внимание на строку КомандаСозданияНаОсновании.ПроверкаПроведенияПередСозданиемНаОсновании = Истина;. Именно здесь БСП жестко и по умолчанию предписывает платформе выполнять проверку проведения для абсолютно всех дополнительных обработок создания связанных объектов. Из-за этого стандартное поведение системы блокирует выполнение нашей обработки, если исходный документ просто записан, но еще не проведен.

Рассмотрим несколько эффективных способов обхода данного ограничения, начиная от правильной доработки логики и заканчивая использованием альтернативных видов обработок БСП.

Решение 1. Использование расширения конфигурации (Рекомендуемый способ)

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

Давайте разберем этот процесс по шагам:

  1. Создадим новое расширение конфигурации (или откроем существующее).
  2. Добавим в расширение общий модуль ВводНаОснованииПереопределяемый.
  3. Создадим процедуру-перехватчик с аннотацией &После.

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


&После("ПередДобавлениемКомандСоздатьНаОсновании")
Процедура ИТМ_ПередДобавлениемКомандСоздатьНаОсновании(ИмяФормы, КомандыСоздатьНаОсновании, СтандартнаяОбработка)
    
    // Проанализируем список команд и найдем команду создания связанных объектов БСП
    Для Каждого Команда Из КомандыСоздатьНаОсновании Цикл
        Если Команда.Идентификатор = "СозданиеСвязанныхОбъектов" Тогда
            // Отключаем принудительную проверку проведения для всех внешних обработок этого типа
            Команда.ПроверкаПроведенияПередСозданиемНаОсновании = Ложь;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

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

Решение 2. Изменение вида внешней обработки на «Заполнение объекта»

Что делать, если у вас нет возможности создавать или изменять расширения конфигурации (например, при работе в закрытых облачных сервисах или на базовых версиях программ)? В этом случае мы можем изменить архитектурный подход к самой обработке.

Вместо использования вида ВидОбработкиСозданиеСвязанныхОбъектов() мы можем зарегистрировать нашу обработку с видом «Заполнение объекта». Рассмотрим, как изменится функция СведенияОВнешнейОбработке():


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
    ПараметрыРегистрации.Информация = НСтр("ru = 'Создание документа ""Передача товаров между организациями"".'");
    
    // Меняем вид обработки на "Заполнение объекта"
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиЗаполнениеОбъекта();
    
    ПараметрыРегистрации.Версия = "1.0";
    ПараметрыРегистрации.БезопасныйРежим = Ложь;
    ПараметрыРегистрации.Назначение.Добавить(Метаданные.Документы.РеализацияТоваровУслуг.ПолноеИмя());
    
    Команда = ПараметрыРегистрации.Команды.Добавить();
    Команда.Представление = НСтр("ru = 'Сформировать передачу товара между организациями.'");
    Команда.Идентификатор = "ИТМ_СозданиеПередачиТоваровМеждуОрганизациями";
    
    // Используем серверный метод вызова
    Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    Команда.ПоказыватьОповещение = Истина;
    
    Возврат ПараметрыРегистрации;
КонецФункции

Выясним, в чем принципиальная разница при таком подходе:

Рассмотрим пример серверного кода в модуле такой обработки:


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

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

Решение 3. Интерактивное создание и открытие формы на клиенте

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

Проанализируем, как организовать вызов клиентского метода. Для этого в процедуре регистрации укажем тип команды ТипКомандыОткрытиеФормы() или ВызовКлиентскогоМетода(). В модуле формы внешней обработки опишем процедуру, которая будет получать данные от сервера без записи нового объекта, а затем открывать его форму на клиенте:


&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения) Экспорт
    
    Если ОбъектыНазначения.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    СсылкаНаОснование = ОбъектыНазначения[0];
    
    // Получим структуру заполнения с сервера
    СтруктураЗаполнения = ПолучитьДанныеДляЗаполненияНаСервере(СсылкаНаОснование);
    
    // Параметры для открытия новой формы
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("ЗначениеЗаполнения", СтруктураЗаполнения);
    
    // Открываем форму нового документа интерактивно
    ОткрытьФорму("Документ.ПередачаТоваровМеждуОрганизациями.ФормаОбъекта", ПараметрыФормы);
    
КонецПроцедуры

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

При таком подходе в модуле менеджера создаваемого документа (в нашем примере это ПередачаТоваровМеждуОрганизациями) в обработчике ОбработкаЗаполнения необходимо предусмотреть корректный разбор переданной структуры из параметра ЗначениеЗаполнения.

Важное предупреждение: Работа с незаписанными объектами

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

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

Мы подробно разобрали внутреннее устройство механизмов БСП, выяснили причину возникновения ошибки обязательного проведения документов и рассмотрели три надежных способа ее обхода. Выбирайте наиболее подходящий вариант под архитектуру вашего проекта и успешно автоматизируйте связанные бизнес-процессы!

← На главную