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