Как программно активировать поле ввода в форме 1С и избежать ошибки «значение не выбрано» при проведении

Программист 1С v7.7 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При разработке внешних обработок в среде 1С:Предприятие (как на классической платформе 7.7, так и на современных версиях 8.x) программисты часто сталкиваются с неприятной ситуацией. Внешний алгоритм успешно открывает форму документа, программно заполняет нужные реквизиты (например, выбирает материально-ответственное лицо или контрагента из справочника), но при попытке записать или провести документ система выдает ошибку: «Документ невозможно провести, т.к. не выбрано значение». При этом визуально в открытой форме значение отлично просматривается в нужном поле ввода, однако фокус на нем отсутствует, и платформа считает его незаполненным.

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

---

Выясняем причину: почему платформа «не видит» программно заполненное поле?

Главная причина кроется в фундаментальном различии между тем, как значение вводит пользователь, и тем, как его присваивает программный код. Рассмотрим этот процесс по шагам.

Когда реальный пользователь работает с формой интерактивно:

  1. Он нажимает на кнопку выбора в поле ввода.
  2. Открывается форма справочника, где выбирается нужный элемент.
  3. Платформа возвращает выбранное значение в форму и инициирует цепочку интерактивных событий. В 1С 7.7 срабатывает формула элемента диалога, а в 1С 8.x — событие ПриИзменении.
  4. Внутри этих обработчиков событий часто зашита важная бизнес-логика: заполнение связанных реквизитов (например, договоров, счетов учета, ставок НДС), взведение скрытых флагов валидации формы или перерасчет сумм.

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

---

Решение №1. Архитектурно правильный подход: проведение документов без открытия формы

Как абсолютно справедливо заметили участники обсуждения в сообщениях 6, 7 и 14, интерактивное открытие экранных форм в цикле для массового редактирования или проведения документов — это серьезный антипаттерн разработки. Такой подход обладает огромными недостатками:

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

Рассмотрим пример реализации такого подхода для платформы 1С:Предприятие 7.7:


// Создаем объект для работы с документами
ДокПКО = СоздатьОбъект("Документ.ПКО");

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

Аналогичный пример для управляемых форм на платформе 1С:Предприятие 8.x выглядит следующим образом:


// Получаем объект документа для редактирования
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();

// Программно заполняем реквизиты
ДокументОбъект.МОЛ = НужныйСотрудник;

// Дублируем логику инициализации из формы
ДокументОбъект.СчетУчетаРасчетов = ПланыСчетов.Хозрасчетный.РасчетыСПодотчетнымиЛицами;

Попытка
    // Записываем документ с принудительным проведением
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    Сообщить("Документ успешно проведен программно!");
Исключение
    Сообщить("Ошибка проведения: " + ОписаниеОшибки());
КонецПопытки;

Этот способ полностью решает проблему «зависшего» фокуса, так как интерфейс формы вообще не задействуется, а скорость выполнения операции возрастает в десятки раз.

---

Решение №2. Трюк с фиктивным закрытием формы для платформы 1С 7.7

Если специфика вашей задачи всё же жестко требует визуального открытия формы (например, если в модуле формы зашиты тысячи строк сложнейшего кода валидации, переносить который в обработку нецелесообразно), нам придется решить задачу активации поля на платформе 1С 7.7.

Для программной передачи фокуса элементу диалога в 1С 7.7 существует стандартный метод Форма.Активизировать(). Однако, если вызвать его напрямую в процедуре ПриОткрытии(), платформа выдаст ошибку или проигнорирует команду, так как элементы интерфейса еще не успели полностью отрисоваться и получить статус активных.

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

Шаг 1. При программном открытии формы из внешней обработки передаем нужное значение (МОЛ) в качестве параметра:


// В коде внешней обработки
ОткрытьФорму("Документ.ПКО", КонтекстФормы, СсылкаНаНужноеМОЛ);

Шаг 2. В модуле формы самого документа перехватываем этот параметр в предопределенной процедуре ПриОткрытии(), заполняем реквизит и инициируем попытку закрытия формы с флагом отложенной активации:


// Переменная модуля формы для хранения состояния активации
Перем ФлагОтложеннойАктивации;

Процедура ПриОткрытии()
    ПараметрПередан = Форма.Параметр;
    Если ПустоеЗначение(ПараметрПередан) = 0 Тогда
        // Заполняем реквизит формы переданным значением
        МОЛ = ПараметрПередан;
        
        // Включаем флаг для последующего перехвата
        ФлагОтложеннойАктивации = 1;
        
        // Вызываем закрытие формы. Переданный "0" означает закрытие без сохранения.
        // Но форма не закроется, так как мы перехватим это событие дальше!
        Форма.Закрыть(0);
    КонецЕсли;
КонецПроцедуры

Шаг 3. В предопределенной процедуре ПриЗакрытии() перехватываем вызов закрытия формы, отменяем его с помощью системной функции СтатусВозврата(0) и безопасно активизируем наше поле ввода, когда все элементы формы уже гарантированно созданы в памяти компьютера:


Процедура ПриЗакрытии()
    Если ФлагОтложеннойАктивации = 1 Тогда
        // Сбрасываем флаг, чтобы не зациклить систему
        ФлагОтложеннойАктивации = 0;
        
        // Отменяем закрытие формы! Она остается открытой на экране.
        СтатусВозврата(0);
        
        // Теперь мы можем безопасно активизировать нужное поле ввода по его идентификатору
        Активизировать("МОЛ");
        Возврат;
    КонецЕсли;
КонецПроцедуры

---

Решение №3. Передача фокуса и вызов событий в управляемых формах 1С 8.x

Если вы работаете в современной конфигурации на управляемых формах 1С:Предприятия 8.3, задача решается более элегантными платформенными механизмами.

Во-первых, чтобы просто передать фокус на конкретное поле ввода, достаточно использовать свойство формы ТекущийЭлемент:


// Передаем фокус ввода на поле "МОЛ" на клиенте
ЭтаФорма.ТекущийЭлемент = Элементы.МОЛ;

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

  1. В модуле управляемой формы документа найдем процедуру, отвечающую за изменение реквизита (например, &НаКлиенте Процедура МОЛПриИзменении(Элемент)).
  2. Добавим к объявлению этой процедуры ключевое слово Экспорт, чтобы сделать её доступной для вызова из других форм и обработок.
  3. Во внешней обработке, после того как мы программно открыли форму и присвоили значение, вызовем этот метод напрямую:

// В модуле внешней обработки
ПараметрыОткрытия = Новый Структура("Ключ", СсылкаНаДокумент);
ФормаДокумента = ОткрытьФорму("Документ.ПриходныйКассовыйОрдер.Форма.ФормаДокумента", ПараметрыОткрытия);

// Программно заполняем реквизит формы
ФормаДокумента.Объект.МОЛ = НужныйСотрудник;

// Вызываем экспортную процедуру обработки изменения реквизита формы
ФормаДокумента.МОЛПриИзменении(ФормаДокумента.Элементы.МОЛ);

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

---

Резюме и рекомендации

Мы подробно проанализировали проблему активации полей ввода и выяснили причины её возникновения. Подведем краткие итоги нашей совместной работы:

← На главную