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