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