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