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

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

Как записать объект из формы в 1С

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

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

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

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

Как устроены данные объекта в форме

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

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

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

Способ 1. Метод формы Записать() — стандартный путь

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

&НаКлиенте

Процедура КомандаЗаписать(Команда)

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

ОповеститьОбИзменении(Объект.Ссылка);

КонецЕсли;

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

Метод возвращает «Истина», если запись прошла успешно, и «Ложь» — если запись не удалась (например, не заполнены обязательные реквизиты или нарушена уникальность). Возвращаемое значение стоит проверять всегда: иначе код продолжит выполняться, хотя объект в базе не изменился.

Для формы документа методу можно передать параметры записи, например режим проведения:

&НаКлиенте

Процедура ЗаписатьИПровести(Команда)

ПараметрыЗаписи = Новый Структура;

ПараметрыЗаписи.Вставить("РежимЗаписи", РежимЗаписиДокумента.Проведение);

Если Записать(ПараметрыЗаписи) Тогда

Закрыть();

КонецЕсли;

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

Порядок действий для стандартной записи
  1. 1
    Убедитесь, что изменённые данные находятся в реквизитах формы (пользователь завершил редактирование полей).
  2. 2
    Вызовите метод формы Записать() с нужными параметрами (для документа — РежимЗаписи).
  3. 3
    Проверьте возвращаемое значение: Истина — записано, Ложь — есть ошибки.
  4. 4
    При успехе оповестите другие формы через ОповеститьОбИзменении() или закройте форму.

Способ 2. Запись через объект на сервере

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

&НаСервере

Процедура ЗаписатьОбъектНаСервере()

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

ОбъектДанных.Записать();

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

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

Обратное преобразование обязательно. Если записать объект и не вернуть его в форму, пользователь увидит старые данные, а при следующей стандартной записи форма перезатрёт изменения, сделанные на сервере. Это одна из самых частых причин «потери» данных при программной записи.

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

Способ 3. Запись через ПолучитьОбъект() по ссылке

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

&НаСервере

Процедура ОбновитьСвязанныйЭлемент(СсылкаНаЭлемент)

СвязанныйОбъект = СсылкаНаЭлемент.ПолучитьОбъект();

СвязанныйОбъект.Комментарий = "Обновлено из формы";

СвязанныйОбъект.Записать();

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

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

Отличия для документов и регистров

Запись и проведение документа

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

Запись регистра сведений

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

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

Метод формы Записать()

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

ДанныеФормыВЗначение + Объект.Записать()

Полный контроль над объектом на сервере: можно менять данные, отключать проверки. Требует обратного преобразования ЗначениеВРеквизитФормы, иначе форма рассинхронизируется.

ПолучитьОбъект() по ссылке

Для записи другого объекта, не открытого в форме. Риск конфликта блокировок, если объект открыт у другого пользователя.

Типичные ошибки и как их избежать

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

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

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

Четвёртая ошибка — рекурсивная запись: вызов Записать() внутри обработчика ПередЗаписью или ПриЗаписи того же объекта. Это приводит к зацикливанию. Если в обработчике нужно дозаполнить данные, меняйте реквизиты объекта напрямую, без повторного вызова записи.

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

Как проверить, что запись прошла

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

Дополнительно можно перечитать объект из базы запросом и сравнить ключевые реквизиты с ожидаемыми. Это особенно полезно при отладке серверных процедур, где изменения происходят «невидимо» для формы.

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

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

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

Истина — объект сохранён в базе

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

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

Объект.Ссылка после записи нового объекта
Норма

Ссылка заполнена, объект существует в базе

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

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

Свойство формы Модифицированность
Норма

Ложь — данные формы синхронны с базой

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

Истина — в форме есть несохранённые изменения

Сообщения пользователю
Норма

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

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

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

Когда стандартной записи недостаточно

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

Маршрут решения

Как выбрать способ

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

Режим ОбменДанными.Загрузка = Истина применяйте только для служебных механизмов и загрузки данных: он отключает бизнес-логику, и записанные так объекты могут нарушить целостность учёта. Для обычной работы пользователя этот режим не предназначен.

Итог

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

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

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

Почему на клиенте не работает Объект.Записать()?

В управляемой форме реквизит «Объект» имеет тип «ДанныеФормыСтруктура» — это представление данных, а не прикладной объект. У него нет метода Записать(). Используйте метод формы Записать() или перенесите логику в серверную процедуру.

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

Передайте в метод формы Записать() структуру с параметром «РежимЗаписи» равным РежимЗаписиДокумента.Проведение. Если проведение не пройдёт (не заполнены данные, нет прав), метод вернёт «Ложь» — это нужно обработать.

Форма после серверной записи показывает старые данные. Что делать?

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

Можно ли записать объект без проверки заполнения реквизитов?

Да, на сервере перед записью установите ОбъектДанных.ОбменДанными.Загрузка = Истина. Но это отключает всю бизнес-логику, включая обработчики записи, поэтому применяйте только для служебной загрузки данных, а не для обычной работы.

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

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

Что делать при ошибке «Конфликт блокировок» при записи?

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