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