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