1с ошибка нельзя изменять поле содержащее объект данных формы
Всем, кто начинает работать с 8.2 после 8.1, не сразу становится понятным, зачем было настолько усложнять работу программистам.
На сервере мы можем выполнить запрос, на управляемую форму бросить таблицу значений, но вот передать результат запроса прямо в таблицу возможности нет. Я решил слегка упростить данный момент для разработки. Вот что у меня получилось.
До тех пор, пока на формы бросались динамические списки или таблицы привязывались к регистрам — все было вроде понятно и разработка шла хорошо, но на определенном этапе понадобилось делать выборки и забрасывать результат именно в таблицы. И тут оказалось, что сделать это выгрузкой результата в таблицу невозможно. В результате в коде начало появляться множество функций &НаСервере, которые, по сути, были идентичные. Читабельность кода от этого не повысилась.
В результате мною коллегам был предложен следующий алгоритм.
В общем модуле, который доступен и серверу и управляемой форме (у меня модуль называется ОбщегоНазначенияКлиентСервер) создаем функцию ВыполнитьЗапросВТаблицу. Она будет выполнять полученный запрос, делать обход результата запроса, формировать структуру из каждой строки результата и каждую заполненную структуру добавлять в массив.
&НаСервере
Функция ВыполнитьЗапросВТаблицу ( ЗапросТекст , СтруктураПараметров ) Экспорт
З = Новый Запрос ( ЗапросТекст );
Для каждого Параметр из СтруктураПараметров Цикл
З . УстановитьПараметр ( Параметр . Ключ , Параметр . Значение );
КонецЦикла;
Результат = З . Выполнить (). Выгрузить ();
СтрокаСтруктуры = «» ;
МассивКолонок = Новый Массив ;
Для каждого Рез из Результат . Колонки Цикл
Если НЕ СтрокаСтруктуры = «» Тогда
СтрокаСтруктуры = СтрокаСтруктуры + «, « ;
КонецЕсли;
СтрокаСтруктуры = СтрокаСтруктуры + Рез . Имя ;
МассивКолонок . Добавить ( Рез . Имя );
КонецЦикла ;
МассивСтрок = Новый Массив ;
Для каждого Рез из Результат Цикл
СтруктураСтроки = Новый Структура ( СтрокаСтруктуры );
Для каждого кол из МассивКолонок Цикл
СтруктураСтроки . Вставить ( кол , Рез [ кол ]);
КонецЦикла;
МассивСтрок . Добавить ( СтруктураСтроки );
КонецЦикла;
Возврат МассивСтрок ;
КонецФункции
В управляемой форме нам понадобится одна функция на &НаСервере на всю форму, которая будет вызывать функцию общего модуля и передавать ей параметры, потому что из процедуры или функции &НаКлиенте управляемой формы сделать это не получается.
Далее нам нужна еще одна процедура на управляемой форме &НаКлиенте, с помощью которой мы будем полученный результат вставлять в таблицу на форме.
Все. Подготовительная часть закончена. После этого для заполнения таблицы значений на управляемой форме результатом запроса нужно в коде вписать вызов последней процедуры следующим образом:
ТекстЗапроса = «ВЫБРАТЬ … далее текст запроса … « ;
СтруктураПараметров = Новый Структура ( «Параметр1, Параметр2, Параметр3» , ЗначениеПараметра1 , ЗначениеПараметра2 , ЗначениеПараметра3 );
// Будем считать, что на форме есть ТаблицаЗначений с именем НашаТаблица
ПоместитьДанныеЗапросаВТаблицу ( ТекстЗапроса , СтруктураПараметров , НашаТаблица );
Важно: Запрос должен быть составлен таким образом, чтобы возвращаемый результат был структурно идентичен нашей таблице значений, то есть типы значений колонок в результате запроса должны совпадать с типами значений колонок нашей таблицы значений и имена колонок в результате запроса должны совпадать с именами колонок таблицы значений.
Вот такой вот алгоритм. Код разжевывать не буду, по-моему там и так все понятно. Мега-гуру просьба не пинать сильно.
Допустим, мы делаем какую-то внешнюю обработку, в форме которой размещаем реквизит с типом "Таблица значений". Назовём этот реквизит СчетаФактуры .
Также на форме размещаем кнопку команды, например, с названием "Обработать счета-фактуры". Задаём действие для команды:
Параметр СчетаФактуры – это наш реквизит формы.
При попытке запустить команду получаем ошибку:
"Нельзя изменять поле, содержащее объект данных формы".
Дело в том, что в параметре процедуры ОбработатьСчетаФактурыНаСервере ( СчетаФактуры ) мы передаём данные формы с типом ДанныеФормыКоллекция , и потом на сервере пытаемся их изменить.
Для исправления ошибки нужно всего навсего объявить переменную и присвоить ей значение реквизита.
Комментарии
Можно сделать так: указать в переменной серверной процедуры директиву "Знач". Т.е. серверная процедура будет иметь вид:
&НаСервере
Процедура ОбработатьСчетаФактурыНаСервере(Знач СчетаФактуры)
Параметр процедуры ОбработатьСчетаФактурыНаСервере передается по ссылке, а значит при завершении процедуры этот параметр возвращается и пытается присвоиться реквизиту, что запрещено.
Ваш совет копирует реквизит в переменную, которая не является реквизитом и поэтому без проблем возвращается. Тот же самый эффект можно получить указав в объявлении процедуры ключевое слово Знач
Процедура ОбработатьСчетаФактурыНаСервере(Знач перем_СчетаФактуры)
Однако таким способом нельзя вернуть из процедуры измененное значение, для этого все-таки необходимо сделать так, как вы указали, а затем использовать процедуру КопироватьДанныеФормы(перем_СчетаФактуры, СчетаФактуры);
СПасибо. Работает.
только имя серверной процедуры на клиенте не совпадает с именем показанной на сервере,
ОбработатьСчетаФактурыНаСервере и СформироватьДвиженияНаСервере.
СПасибо. Работает.
только имя серверной процедуры на клиенте не совпадает с именем показанной на сервере,
ОбработатьСчетаФактурыНаСервере и СформироватьДвиженияНаСервере.
А я то думал "Что за хрень!?".. Переносил блок в разные места.. А тут такое..
А я то думал "Что за хрень!?".. Переносил блок в разные места.. А тут такое..
РеквизитФормыВЗначение() является методом управляемой формы, компилируется только &НаСервере, контекст формы является для него необходимым, поэтому компиляция &НаСервереБезКонтекста недоступна. На клиенте не работает, поскольку в результате получаем прикладной объект.
Где и когда его нужно использовать?
Метод РеквизитФормыВЗначение() необходим, если требуется из модуля формы вызвать стандартный метод объекта или метод (процедуру, функцию) из модуля объекта, из общего модуля.
Этот метод будет отрабатывать с данными, взятыми из формы, т.е. еще не записанными в базу.
Для того, чтобы измененные данные вернуть на форму, используется метод ЗначениеВРеквизитФормы().
Вторым параметром метода РеквизитФормыВЗначение является тип значения. Это необязательный параметр. Если обрабатываемый реквизит не является составным типом, то тип будет автоматически получен из реквизита формы. Иначе генерируется исключение времени выполнения.
&НаСервере
Процедура ЗаполнитьТЧПоДаннымПоследнегоДокумента(Контрагент)
ДокОбъект = РеквизитФормыВЗначение(«Объект»);
ДокОбъект.ЗаполнитьТЧПоДаннымПоследнегоДокумента(Контрагент);
ЗначениеВРеквизитФормы(ДокОбъект, «Объект»);
&НаСервере
Процедура ОчиститьСтрокиСПустойЦенойНаСервере()
ДокОбъект = РеквизитФормыВЗначение(«Объект»);
ДокОбъект.ОчиститьСтрокиСПустойЦеной();
ЗначениеВРеквизитФормы(ДокОбъект, «Объект»);
В указанных выше случаях метод РеквизитФормыВЗначение() похож на метод ПолучитьОбъект(). Разница в том, что первый получает объект, заполненный данными формы, а второй — объект с данными из базы.
ДанныеФормыВзначение()
В отличие от метода управляемой формы РеквизитФормыВЗначение(), процедура глобального контекста ДанныеФормыВЗначение() может работать без контекста формы, но именно поэтому, в частности, ей необходимо указывать тип конвертируемых данных.
Обратной процедурой является ЗначениеВДанныеФормы().
// Инициализация набора констант
НаборКонстантОбъект = ДанныеФормыВЗначение(НаборКонстант, Тип(«КонстантыНабор»));
НаборКонстантОбъект.Прочитать();
ЗначениеВДанныеФормы(НаборКонстантОбъект, НаборКонстант);
Набор = ДанныеФормыВЗначение(НаборФорма, Тип(«РегистрБухгалтерииНаборЗаписей.РегистрУправленческий»));
Если Набор.Количество()=0 Тогда
Возврат ;
КонецЕсли;
Набор.УстановитьАктивность(НЕ Набор[0].Активность);
ЗначениеВДанныеФормы(Набор, НаборФорма);
В этом примере на форме отображен набор записей регистра бухгалтерии. При отработке процедуры, активность набора меняется только на форме, в базе при этом ничего не происходит. Активность у набора записей регистра в базе поменяется только после выполнения операции Записать.
3)
&НаКлиенте
Процедура Заполнить(Команда)
ОснРеквизит = Объект;
ВыполнитьНаСервереБезКонтекста(ОснРеквизит);
КопироватьДанныеФормы(ОснРеквизит,Объект)
&НаСервереБезКонтекста
Процедура ВыполнитьНаСервереБезКонтекста(ОснРеквизит)
ОбъектЗначение = ДанныеФормыВЗначение(ОснРеквизит, Тип(«ДокументОбъект.АктСверкиВзаиморасчетов»));
//…Выполнение операций с Объектом «ОбъектЗначение»
В этом примере на клиент возвращается ОснРеквизит, содержащий измененные данные. Но его еще нужно «запихать» в отображаемую форму. Для этого используем процедуру глобального контекста КопироватьДанныеФормы().
В большинстве случаев процедуры ДанныеФормыВзначение() и РеквизитФормыВЗначение() взаимозаменяемы. При этом РеквизитФормыВЗначение проще в использовании. Но если требуется использование &НаСервереБезКонтекста — тогда только ДанныеФормыВЗначение.
В качестве иллюстрации взаимозаменяемости Пример 2 еще в двух вариантах:
ВАРИАНТ 1
&НаКлиенте
Процедура Активность(Команда)
ОснРеквизитДвижения = Объект.Движения.РегистрУправленческий;
АктивностьНаСервереБезКонтекста(ОснРеквизитДвижения);
КопироватьДанныеФормы(ОснРеквизитДвижения,Объект.Движения.РегистрУправленческий);
&НаСервереБезКонтекста
Процедура АктивностьНаСервереБезКонтекста(ОснРеквизитДвижения)
Набор = ДанныеФормыВЗначение(ОснРеквизитДвижения, Тип(«РегистрБухгалтерииНаборЗаписей.РегистрУправленческий»));
Если Набор.Количество()=0 Тогда
Возв рат;
КонецЕсли;
Набор.УстановитьАктивность(НЕ Набор[0].Активность);
ЗначениеВДанныеФормы(Набор, ОснРеквизитДвижения);
ВАРИАНТ 2
&НаСервере
Процедура АктивностьНаСервере()
НаборФорма = РеквизитФормыВЗначение(«Объект»);
Набор = НаборФорма.Движения.РегистрУправленческий;
Если Набор.Количество()=0 Тогда
Возврат;
КонецЕсли;
Набор.УстановитьАктивность(НЕ Набор[0].Активность);
ЗначениеВРеквизитФормы(НаборФорма, «Объект»);
Ответ: Чтобы открыть форму нам нужно связать ее с ссылкой объекта, но объект еще не записан. Многие пытаются использовать метод УстановитьСсылкуНового () но это не прокатывает. После долгих чтений я сделал вот так:
&НаКлиенте
Процедура Команда ( Команда )
Форма = ПолучитьФорму ( «Документ.НашДокумент .ФормаОбъекта» );
ДанныеФормы = Форма.Объект ; // Получаем объект формы в переменную
ЗаполнитьДокументНаСервере ( ДанныеФормы ); // Заполняем документ на сервере
КопироватьДанныеФормы ( ДанныеФормы, Форма.Объект ); // копируем наш объект в объект формы и далее открываем ее
Форма.Открыть ();
// Можно использовать безконтекстный вызов, оптимизируем передачу данных
Функция ЗаполнитьДокументНаСервере ( ДанныеФормы );
Док = ДанныеФормыВЗначение ( ДанныеФормы , Тип ( «ДокументОбъект.НашДокумент» )); // Получаем объект из данных формы ИЛИ
// Заполняем реквизиты объекта или другие действия ********
ЗначениеВДанныеФормы ( Док,ДанныеФормы ); // Кладем обратно в объект формы уже созданный документ
2. Как открыть форму уже записанного документа?
Здесь есть два метода ОткрытьФорму () или ОткрытьЗначение () . В обоих случаях нужно передавать ссылку на записанный документ.
&НаКлиенте
Процедура Команда ( Команда )
СсылкаОбъекта = СоздатьНаСервере ();
//1.
ОткрытьФорму ( «Документ.НашДокумент.Егоформа» , Новый Стурктура ( «Ключ» , СсылкаОбъекта )); // Через параметры мы передаем ссылку документа
&НаСервере
Функция ЗаполнитьДокументНаСервере ()
// Используем метод создать
НовыйОбъект = Документы . НашДокумент . СоздатьДокумент ();
НовыйОбъект . Записать (); // Обязательно записываем
Возврат НовыйОбъект . Ссылка ; //Возвращаем ссылку
На уникальность материла не претендую, сам только «начинающий» так что если что не так — критикуйте!
Ответ: Чтобы открыть форму нам нужно связать ее с ссылкой объекта, но объект еще не записан. Многие пытаются использовать метод УстановитьСсылкуНового () но это не прокатывает. После долгих чтений я сделал вот так:
&НаКлиенте
Процедура Команда ( Команда )
Форма = ПолучитьФорму ( «Документ.НашДокумент .ФормаОбъекта» );
ДанныеФормы = Форма.Объект ; // Получаем объект формы в переменную
ЗаполнитьДокументНаСервере ( ДанныеФормы ); // Заполняем документ на сервере
КопироватьДанныеФормы ( ДанныеФормы, Форма.Объект ); // копируем наш объект в объект формы и далее открываем ее
Форма.Открыть ();
// Можно использовать безконтекстный вызов, оптимизируем передачу данных
Функция ЗаполнитьДокументНаСервере ( ДанныеФормы );
Док = ДанныеФормыВЗначение ( ДанныеФормы , Тип ( «ДокументОбъект.НашДокумент» )); // Получаем объект из данных формы ИЛИ
// Заполняем реквизиты объекта или другие действия ********
ЗначениеВДанныеФормы ( Док,ДанныеФормы ); // Кладем обратно в объект формы уже созданный документ
2. Как открыть форму уже записанного документа?
Здесь есть два метода ОткрытьФорму () или ОткрытьЗначение () . В обоих случаях нужно передавать ссылку на записанный документ.
&НаКлиенте
Процедура Команда ( Команда )
СсылкаОбъекта = СоздатьНаСервере ();
//1.
ОткрытьФорму ( «Документ.НашДокумент.Егоформа» , Новый Стурктура ( «Ключ» , СсылкаОбъекта )); // Через параметры мы передаем ссылку документа
&НаСервере
Функция ЗаполнитьДокументНаСервере ()
// Используем метод создать
НовыйОбъект = Документы . НашДокумент . СоздатьДокумент ();
НовыйОбъект . Записать (); // Обязательно записываем
Возврат НовыйОбъект . Ссылка ; //Возвращаем ссылку
На уникальность материла не претендую, сам только «начинающий» так что если что не так — критикуйте!
Читайте также: