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