К статье
Практический материал

Разработка и Конфигурация · 14 мин

1С: как записать объект из управляемой формы

Как записать объект из управляемой формы в 1С: Записать(), РеквизитФормыВЗначение, запись без закрытия, обработка ошибок. Примеры кода и типичные ошибки.

Запись объекта из управляемой формы — одна из базовых операций в разработке на платформе 1С:Предприятие 8.3. В отличие от обычных форм, в управляемых формах действует разделение контекста на клиентский и серверный, поэтому непосредственный вызов метода Записать() у объекта возможен только на сервере. Понимание того, как платформа передает данные между клиентом и сервером, помогает избежать типовых ошибок вроде «Метод объекта не обнаружен» или потери введенных пользователем данных.

В большинстве случаев разработчику вообще не нужно писать код записи: если основной реквизит формы имеет тип ДанныеФормыСтруктура на основе объекта (справочника, документа), платформа сама выполняет запись при нажатии кнопок «Записать» и «Записать и закрыть». Собственный код требуется, когда нужно записать объект программно в ходе другой операции, записать несколько связанных объектов за одну транзакцию или сохранить данные, не привязанные к основному реквизиту формы.

Ниже разобраны основные способы записи: штатная запись реквизита формы, программная запись через преобразование данных формы в объект, запись из клиентского контекста с вызовом сервера, а также обработка ошибок и проверка результата. Примеры приведены на встроенном языке 1С и применимы к типовым и самописным конфигурациям на управляемых формах.

Как устроена запись данных в управляемой форме

Управляемая форма не работает с прикладным объектом напрямую. Данные объекта сериализуются в специальный тип ДанныеФормыСтруктура (или ДанныеФормыКоллекция для табличных частей), который и отображается на клиенте. Сам объект справочника или документа существует на сервере только в момент серверного вызова — например, при чтении формы или при записи.

Из этого следуют два ключевых ограничения. Первое: методы прикладных объектов (Записать(), Заполнить() и другие) недоступны в клиентском контексте. Второе: изменения, которые пользователь внес в поля формы, существуют только в данных формы, пока не выполнено преобразование в объект и запись. Если закрыть форму без записи, данные будут потеряны — платформа лишь предложит стандартный вопрос о сохранении.

Понимание этого механизма объясняет, почему попытка написать Объект.Записать() в клиентской процедуре приводит к ошибке: Объект на клиенте — это данные формы, а не прикладной объект, и метода Записать() у него нет.

Штатная запись через команду формы

Если у формы заполнено свойство «Основной реквизит» (обычно реквизит с именем Объект типа ДанныеФормыСтруктура), платформа автоматически предоставляет команды «Записать», «Записать и закрыть», «Провести», «Провести и закрыть» — в зависимости от типа объекта. Эти команды работают без единой строки кода: платформа сама выполняет серверный вызов, преобразует данные формы в объект, вызывает Записать() и обновляет данные формы.

Программно вызвать штатную запись можно методом формы Записать() — он доступен и на клиенте, и на сервере:

&НаКлиенте

Процедура КомандаСохранить(Команда)

Если Не Записать() Тогда

Возврат; // запись не выполнена, пользователь увидел сообщения об ошибках

КонецЕсли;

Оповестить("ЗаписанЭлементСправочника", Объект.Ссылка);

КонецПроцедуры

Метод возвращает Истина, если запись прошла успешно, и Ложь при ошибке или отказе. Параметром можно передать структуру с параметрами записи, например Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение) для проведения документа. Это предпочтительный способ, когда нужно просто сохранить объект формы и выполнить дополнительные действия после успешной записи.

Программная запись через РеквизитФормыВЗначение

Когда требуется доработать объект перед записью — заполнить скрытые реквизиты, изменить данные, отсутствующие на форме, — используется явное преобразование данных формы в прикладной объект. Вся работа выполняется в серверной процедуре формы:

&НаСервере

Процедура ЗаписатьСДополнениемНаСервере()

ОбъектДляЗаписи = РеквизитФормыВЗначение("Объект");

ОбъектДляЗаписи.Комментарий = "Записан программно";

ОбъектДляЗаписи.Записать();

ЗначениеВРеквизитФормы(ОбъектДляЗаписи, "Объект");

КонецПроцедуры

Обратное преобразование ЗначениеВРеквизитФормы обязательно, если после записи форма остается открытой: оно переносит в данные формы изменения, которые внесла запись (например, присвоенный номер документа, дату, признак проведения). Без этого шага пользователь увидит устаревшие данные, а повторная запись может привести к конфликту версий объекта.

Для записи документа в режиме проведения у метода Записать() объекта указывается режим: ОбъектДляЗаписи.Записать(РежимЗаписиДокумента.Проведение). Если проведение не удалось, объект остается записанным в режиме простой записи — это штатное поведение платформы, которое нужно учитывать при проверке результата.

Порядок программной записи объекта
  1. 1
    В клиентской процедуре вызовите серверную процедуру формы
  2. 2
    На сервере выполните РеквизитФормыВЗначение("Объект") и получите прикладной объект
  3. 3
    При необходимости измените реквизиты объекта
  4. 4
    Вызовите Записать() внутри конструкции Попытка/Исключение
  5. 5
    Выполните ЗначениеВРеквизитФормы для обновления данных формы
  6. 6
    Верните на клиент признак успеха и ссылку на объект

Запись из клиентского контекста и запись без закрытия формы

Частая задача — записать объект по кнопке, не закрывая форму, и продолжить работу. Для этого клиентская процедура вызывает серверную, а та возвращает результат. Важно, чтобы серверная процедура была объявлена с директивой &НаСервере (без НаКлиентеНаСервереБезКонтекста, если нужен доступ к реквизитам формы):

&НаКлиенте

Процедура ЗаписатьБезЗакрытия(Команда)

Результат = ЗаписатьОбъектНаСервере();

Если Результат.Успех Тогда

ПоказатьОповещениеПользователя("Записано", ,

ПолучитьНавигационнуюСсылку(Результат.Ссылка));

Иначе

ПоказатьПредупреждение(, Результат.ТекстОшибки);

КонецЕсли;

КонецПроцедуры

&НаСервере

Функция ЗаписатьОбъектНаСервере()

Результат = Новый Структура("Успех, Ссылка, ТекстОшибки", Ложь, Неопределено, "");

Попытка

ОбъектДляЗаписи = РеквизитФормыВЗначение("Объект");

ОбъектДляЗаписи.Записать();

ЗначениеВРеквизитФормы(ОбъектДляЗаписи, "Объект");

Результат.Успех = Истина;

Результат.Ссылка = ОбъектДляЗаписи.Ссылка;

Исключение

Результат.ТекстОшибки = ОписаниеОшибки();

КонецПопытки;

Возврат Результат;

КонецФункции

Обратите внимание: исключение перехватывается на сервере, а текст ошибки возвращается на клиент строкой. Пробрасывать само исключение дальше тоже допустимо — тогда платформа покажет стандартное окно ошибки, — но управляемая обработка дает более понятное поведение для пользователя.

Запись нового объекта и работа со ссылкой

При записи нового элемента ссылка объекту присваивается в момент первой записи (либо заранее через УстановитьСсылкуНового, если ссылка нужна до записи — например, для связи с другими объектами). После успешной записи данные формы обновляются, и Объект.Ссылка становится заполненной.

Если нужно получить ссылку нового объекта до записи, используется типовой прием на сервере:

ОбъектДляЗаписи = РеквизитФормыВЗначение("Объект");

Если ОбъектДляЗаписи.ЭтоНовый() Тогда

НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор);

ОбъектДляЗаписи.УстановитьСсылкуНового(НоваяСсылка);

КонецЕсли;

ОбъектДляЗаписи.Записать();

После записи нового объекта форма автоматически переходит из режима создания в режим редактирования: меняется заголовок, становится доступной навигационная ссылка. Если запись выполнялась штатным методом формы, эти изменения происходят без дополнительного кода.

Обработка ошибок и проверка результата записи

Запись может завершиться отказом по многим причинам: не заполнены обязательные реквизиты, сработала проверка в подписке на событие ОбработкаПроверкиЗаполнения, нарушена уникальность кода, объект изменен другим пользователем (конфликт блокировок). Надежный код записи всегда анализирует результат.

Основные приемы проверки: анализ возвращаемого значения штатного метода Записать() формы, перехват исключений через Попытка/Исключение при программной записи, а также контроль сообщений пользователю через ПолучитьСообщенияПользователю(). Для диагностики проблем с заполнением полезно вызывать ПроверитьЗаполнение() у объекта до вызова Записать() — метод вернет Ложь и сформирует сообщения без попытки физической записи.

Признак успешной записи — отсутствие исключения, заполненная ссылка и сброшенный признак модифицированности формы (Модифицированность = Ложь). Если после записи форма по-прежнему считается измененной, скорее всего, пропущено обратное преобразование ЗначениеВРеквизитФормы либо код после записи снова изменил данные формы.

Контрольные точки

Как проверить результат записи

Возвращаемое значение Записать()
Норма

Истина, сообщений об ошибках нет

Тревожный признак

Ложь или исключение — читать сообщения пользователю

Ссылка объекта после записи
Норма

Ссылка заполнена, ЭтоНовый() = Ложь

Тревожный признак

Пустая ссылка — запись не выполнена

Признак Модифицированность формы
Норма

Сброшен после записи

Тревожный признак

Остался Истина — пропущено ЗначениеВРеквизитФормы

Данные в базе
Норма

Запрос по ссылке возвращает актуальные реквизиты

Тревожный признак

Данные старые — запись прошла в обход формы или не зафиксирована транзакция

Типичные ошибки при записи из управляемой формы

Первая и самая распространенная ошибка — попытка вызвать Объект.Записать() в клиентской процедуре. Платформа сообщает «Метод объекта не обнаружен (Записать)», потому что на клиенте доступны только данные формы. Решение — перенести запись в серверную процедуру.

Вторая ошибка — преобразование РеквизитФормыВЗначение без обратного ЗначениеВРеквизитФормы. Форма продолжает показывать старые данные, а при следующей штатной записи пользователь молча перезаписывает изменения, сделанные программно. Третья типичная проблема — запись объекта через Ссылка.ПолучитьОбъект() параллельно с открытой формой того же объекта: при сохранении формы возникает конфликт версий, и пользователь получает ошибку «Данные были изменены другим пользователем».

Отдельно стоит упомянуть запись в обработчиках ожидания и фоновых операциях на клиенте: любая попытка обратиться к прикладному объекту там приведет к ошибке компиляции или выполнения. Все операции с объектами — только на сервере, а клиент получает обратно простые типы: строки, булево, ссылки.

Типичные ошибки
01
Вызов Объект.Записать() на клиенте — ошибка «Метод объекта не обнаружен», запись не выполняется
02
Пропуск ЗначениеВРеквизитФормы после программной записи — форма показывает устаревшие данные и перезаписывает изменения
03
Запись без Попытка/Исключение — пользователь видит техническое окно ошибки вместо понятного сообщения
04
Изменение объекта через ПолучитьОбъект() при открытой форме — конфликт версий при сохранении
05
Запись нескольких объектов без транзакции — несогласованные данные при сбое

Выбор способа записи под конкретную задачу

Если нужно просто сохранить объект формы — используйте штатный метод формы Записать(), это самый короткий и надежный путь. Если перед записью требуется программно дозаполнить объект — берите связку РеквизитФормыВЗначение / Записать() / ЗначениеВРеквизитФормы. Если записываются данные, вообще не связанные с основным реквизитом формы (например, регистр сведений по данным формы обработки), создавайте объект записи в отдельной серверной процедуре без привязки к реквизитам формы.

Для массовой записи из формы списка или обработки не используйте данные формы вовсе: работайте напрямую с менеджерами объектов (Справочники.Имя.СоздатьЭлемент(), Документы.Имя.СоздатьДокумент()) на сервере, оборачивая пакет в транзакцию. Механизм преобразования реквизитов формы предназначен именно для интерактивной работы с одним объектом.

Сравнение способов записи

Штатный Записать() формы

Подходит для обычного сохранения объекта формы; не позволяет доработать объект перед записью

РеквизитФормыВЗначение + Записать()

Полный контроль над объектом на сервере; требует обратного преобразования и обработки ошибок

Запись через менеджер объекта

Для фоновых и массовых операций вне формы; не связан с данными открытой формы и может вызвать конфликт версий

Итог

Что важно запомнить

В управляемой форме объект записывается только на сервере. Для простого сохранения достаточно штатного метода формы Записать(), для доработки объекта — связки РеквизитФормыВЗначение, Записать(), ЗначениеВРеквизитФормы. Всегда обрабатывайте ошибки, проверяйте результат и обновляйте данные формы после программной записи.

Частые вопросы

Почему возникает ошибка «Метод объекта не обнаружен (Записать)»?

Ошибка означает, что Записать() вызван там, где доступны только данные формы, а не прикладной объект — чаще всего в клиентской процедуре. На клиенте Объект имеет тип ДанныеФормыСтруктура. Перенесите запись в серверную процедуру и используйте РеквизитФормыВЗначение, либо вызывайте метод формы Записать(), который доступен на клиенте.

Как записать документ с проведением из формы?

Два варианта: вызвать штатный метод формы с параметром Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение)) либо на сервере получить объект через РеквизитФормыВЗначение и вызвать ОбъектДляЗаписи.Записать(РежимЗаписиДокумента.Проведение). Учтите, что при неудачном проведении документ останется просто записанным.

Как записать объект, чтобы форма не закрывалась?

Штатные команды «Записать» и «Провести» не закрывают форму — закрывает только «Записать и закрыть». Программно вызывайте Записать() без последующего вызова Закрыть(), и форма останется открытой в режиме редактирования записанного объекта.

Что делать при ошибке «Данные были изменены другим пользователем»?

Конфликт версий возникает, если объект изменили вне формы после ее открытия — другим сеансом или программно через ПолучитьОбъект(). Решение: перечитать объект (команда «Перечитать» формы) и повторить изменения, либо устранить параллельную запись в обход открытой формы.

Можно ли записать объект в фоновом задании из формы?

Да, но в фоновое задание нельзя передать данные формы или контекст формы. Передайте в параметрах задания ссылку или структуру с простыми типами, а внутри фоновой процедуры работайте с объектом через менеджер (ПолучитьОбъект, СоздатьЭлемент). Результат верните через СообщитьПрогресс или запись в хранилище, и обновите форму после завершения.

Как проверить заполнение объекта без фактической записи?

На сервере получите объект через РеквизитФормыВЗначение и вызовите ПроверитьЗаполнение(). Метод вернет Истина или Ложь и сформирует сообщения пользователю, привязанные к полям формы, не выполняя физическую запись в базу данных.