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