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