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