Как вызвать форму при нажатии кнопки ОК и корректно передать в нее ссылку на документ

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

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

В данной статье мы разберем, почему так происходит, проанализируем механизмы работы с контекстом в обычных формах и рассмотрим наиболее надежные способы передачи данных между объектами.

Почему ссылка становится пустой при нажатии кнопки ОК

Разберем ситуацию, описанную в исходном вопросе. Автор пытается вызвать форму лога в событии ПриЗаписи, устанавливая Отказ = Истина. Это стандартный прием для прерывания записи, но он несет в себе скрытую проблему для контекста формы.

Когда мы нажимаем ОК, система инициирует транзакцию записи. Если документ новый, то полноценная ссылка в базе данных еще не создана. Устанавливая Отказ = Истина, мы отменяем фиксацию транзакции. В результате, когда открытая форма пытается обратиться к свойству ЭтаФорма.ВладелецФормы.Ссылка, она может получить пустое значение, так как объект в памяти владельца находится в «подвешенном» состоянии, а физически в базе данных под этой ссылкой ничего нет или связь между формами была нарушена из-за особенностей жизненного цикла обычных форм.

Проанализируем также запрос, который использовал автор. Использование конструкции ГДЕ КорректировкаРеализацииТовары.Ссылка.Ссылка = &Ссылка является избыточным. Кроме того, если данные еще не записаны в базу (так как мы отменили запись), запрос к табличной части документа через базу данных вернет пустой результат. Для получения актуальных данных из незаписанного объекта нужно оперировать не запросами к БД, а передавать саму объектную технику.

Решение №1: Передача ссылки через экспортную переменную (рекомендуемое)

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

Рассмотрим пошагово, как это реализовать. Сначала подготовим вызываемую форму (в нашем примере это форма регистра сведений ЛогРедактированияДокументов).

В самом верху модуля вызываемой формы объявим экспортную переменную:

Перем ПереданнаяСсылка Экспорт;

Теперь в основной форме документа (например, КорректировкаРеализации) изменим процедуру вызова. Вместо ОткрытьФорму() воспользуемся более гибким подходом:


Процедура ПриЗаписи(Отказ)
    
    // Прерываем стандартную запись, чтобы показать свое окно
    Отказ = Истина;
    
    // Получаем форму, но пока не открываем её
    ИмяФормы = "РегистрСведений.ЛогРедактированияДокументов.Форма.КорректировкаРеализации";
    ФормаЛога = ПолучитьФорму(ИмяФормы, , ЭтаФорма);
    
    // Явно передаем ссылку в экспортную переменную формы
    ФормаЛога.ПереданнаяСсылка = ЭтотОбъект.Ссылка;
    
    // Теперь открываем форму
    ФормаЛога.Открыть();
    
КонецПроцедуры

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

Решение №2: Использование передачи всего объекта

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

В модуле вызываемой формы объявим переменную для объекта:

Перем ДокументОбъект Экспорт;

В форме документа передадим ЭтотОбъект:


Процедура ПриЗаписи(Отказ)
    Отказ = Истина;
    
    ФормаЛога = ПолучитьФорму("ПутьКФорме", , ЭтаФорма);
    // Передаем весь объект со всеми данными на форме
    ФормаЛога.ДокументОбъект = ЭтотОбъект; 
    ФормаЛога.Открыть();
КонецПроцедуры

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

Решение №3: Организация логики «Второго прохода»

Рассмотрим ситуацию, когда после заполнения формы лога нам все-таки нужно записать документ. Чтобы не попасть в бесконечный цикл (нажали ОК -> сработало ПриЗаписи -> открылась форма лога -> нажали ОК в логе -> программно вызвали запись документа -> снова сработало ПриЗаписи), необходимо использовать флаг-признак.

Проанализируем пример реализации такого флага в модуле формы документа:


Перем ЛогЗаполнен; // Локальная переменная модуля формы

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

// В этой процедуре (ее нужно вызвать из формы лога) устанавливаем флаг
Процедура ЗавершитьЗаписьПослеЛога() Экспорт
    ЛогЗаполнен = Истина;
    ЭтотОбъект.Записать(); // Теперь запись пройдет успешно
КонецПроцедуры

В форме лога, при нажатии на кнопку ОК, мы вызываем экспортную процедуру владельца:


Процедура ОКНажатие(Элемент)
    // Проверка заполнения данных лога...
    // ... запись в регистр логов ...
    
    // Уведомляем форму документа, что можно записываться
    ЭтаФорма.ВладелецФормы.ЗавершитьЗаписьПослеЛога();
    Закрыть();
КонецПроцедуры

Важные рекомендации по работе с запросами

Посмотрим на пример запроса из исходного сообщения. Автор использует ЛЕВОЕ СОЕДИНЕНИЕ с остатками. Важно помнить:

  1. Параметр Период: В виртуальных таблицах остатков всегда указывайте параметр &Период. Если документ редактируется «задним числом», вам могут понадобиться остатки на момент документа, а не на текущее время.
  2. Фильтрация: Старайтесь передавать список номенклатуры в параметры виртуальной таблицы: ТоварыНаСкладах.Остатки(&Период, Номенклатура В (&СписокНоменклатуры)). Это значительно ускоряет работу системы.
  3. Использование Ссылки: Для проверки условий в запросе используйте ГДЕ Ссылка = &Ссылка. Лишнее обращение через точку Ссылка.Ссылка заставляет платформу выполнять дополнительное соединение с таблицей самого документа, что снижает производительность.

Подводя итог, можно сказать, что главная причина «пропадания» ссылки — это попытка использовать контекст владельца в момент отмены транзакции записи. Используя метод ПолучитьФорму() и явную передачу параметров в экспортные переменные, вы сделаете свой код более надежным и предсказуемым.

← На главную