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