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

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

Как программно сохранить документ в 1С

Как программно записать документ в 1С: методы Записать(), режимы записи и проведения, обработка ошибок, транзакции и готовые примеры кода на встроенном языке.

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

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

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

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

Документ в 1С существует в двух состояниях: как объект в памяти (тип ДокументОбъект) и как запись в базе данных. Пока вы не вызвали Записать(), все изменения живут только в памяти и не видны другим пользователям. Метод Записать() переносит объект в базу и при необходимости сразу выполняет проведение.

Сигнатура метода: Записать(РежимЗаписи, РежимПроведения). Первый параметр принимает значения РежимЗаписиДокумента.Запись, Проведение или ОтменаПроведения. Второй параметр — РежимПроведенияДокумента.Оперативный или Неоперативный — актуален только для документов с оперативным проведением.

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

Создание и запись нового документа

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

НовыйДок = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

НовыйДок.Дата = ТекущаяДата();

НовыйДок.Контрагент = КонтрагентСсылка;

НовыйДок.Организация = ОрганизацияСсылка;

СтрокаТовара = НовыйДок.Товары.Добавить();

СтрокаТовара.Номенклатура = ТоварСсылка;

СтрокаТовара.Количество = 2;

НовыйДок.Записать();

Номер документа при записи присваивается автоматически согласно нумератору, если реквизит Номер не заполнен вручную. Ручная установка номера возможна, но чревата конфликтами нумерации и ошибками уникальности — без явной необходимости лучше этого не делать.

После записи ссылка на созданный документ доступна через НовыйДок.Ссылка. Именно её обычно возвращают из функции или выводят в сообщение для контроля.

bsl
НовыйДок = Документы.ПриходнаяНакладная.СоздатьДокумент();
НовыйДок.Дата = ТекущаяДата();
НовыйДок.Записать(РежимЗаписиДокумента.Запись);
Сообщить("Создан документ: " + НовыйДок.Ссылка);

Изменение и перезапись существующего документа

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

ДокОбъект = ДокументСсылка.ПолучитьОбъект();

ДокОбъект.Комментарий = "Изменён программно";

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

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

Для проведённых документов повторная запись в режиме Запись не отменяет проведение — документ остаётся проведённым. Чтобы снять его с проведения, явно вызывают Записать(РежимЗаписиДокумента.ОтменаПроведения).

Программное проведение документа

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

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

Перед проведением полезно программно проверить заполненность обязательных реквизитов. Метод ПроверитьЗаполнение() возвращает Истина, если контроль пройден, и Ложь с выводом сообщений — если нет. Это позволяет заранее отсеять документы, которые гарантированно не проведутся.

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

Обработка ошибок и транзакции

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

Попытка

ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);

Исключение

Сообщить("Не удалось провести " + ДокОбъект.Ссылка

+ ": " + ОписаниеОшибки());

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

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

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

Контекст выполнения: клиент, сервер, фоновое задание

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

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

Отдельный случай — обмен данными. Если документ записывается в обработчике загрузки и не должен регистрироваться к выгрузке в другие узлы, перед записью устанавливают ДокОбъект.ОбменДанными.Загрузка = Истина. Этот же приём отключает часть проверок, поэтому вне обмена использовать его «для ускорения» не стоит — можно записать некорректные данные.

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

Как проверить, что документ действительно сохранён

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

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

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

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

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

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

Ссылка не пустая, документ открывается в списке

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

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

Признак Проведен
Норма

Равен Истина при проведении

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

Ложь — документ записан, но не проведён

Движения по регистрам
Норма

Наборы записей содержат строки

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

Движений нет — проверьте ОбработкаПроведения

Журнал ошибок
Норма

Нет записей об исключениях

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

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

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

Чем отличается Записать() от Записать(РежимЗаписиДокумента.Проведение)?

Вызов без параметров только сохраняет документ в базе без формирования движений по регистрам. Режим «Проведение» дополнительно запускает обработчик проведения и записывает движения. Если документ уже был проведён, повторный вызов с режимом «Проведение» перепроводит его.

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

Получите объект через ПолучитьОбъект() и вызовите Записать(РежимЗаписиДокумента.ОтменаПроведения). Движения по регистрам будут удалены, сам документ останется в базе в состоянии «не проведён». Удалить документ полностью можно методом УстановитьПометкуУдаления(Истина).

Почему возникает ошибка «Конфликт блокировок при выполнении транзакции»?

Документ в этот момент редактирует другой пользователь или фоновое задание. В массовых обработках такие ситуации перехватывают через Попытка/Исключение, документ пропускают или ставят в очередь на повторную обработку. Уменьшить частоту конфликтов помогает выполнение обработки в нерабочее время и короткие транзакции.

Можно ли записать документ, минуя проверки заполнения?

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

Как записать документ из внешней обработки в файловой базе?

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

Нужно ли перенумеровывать документы после программного создания?

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

Итог

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

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