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

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

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

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

Программное изменение документа в 1С — одна из базовых задач разработчика и администратора: обработки массовой корректировки, обмены данными, интеграции с внешними системами и регламентные задания опираются именно на этот механизм. При этом ошибка в коде способна испортить учётные данные: перезаписать реквизиты, сбить проведение или оставить документ в несогласованном состоянии.

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

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

Как получить объект документа по ссылке

В 1С существует два разных представления документа: ссылка (объект типа ДокументСсылка) и объект (ДокументОбъект). Ссылка — это лишь указатель на запись в базе, она доступна только для чтения. Чтобы что-то поменять, нужно материализовать объект в памяти.

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

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

Если ДокументСсылка.Пустая() Тогда

ВызватьИсключение "Документ не найден";

КонецЕсли;

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

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

Изменение реквизитов и табличных частей

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

ДокументОбъект.Комментарий = "Скорректировано обработкой";

ДокументОбъект.Склад = СкладСсылка;

ДокументОбъект.Дата = ТекущаяДатаСеанса();

Табличные части представлены коллекциями строк. Можно перебрать строки, изменить значения в конкретной строке, добавить новую или удалить существующую. Очистка всей табличной части выполняется методом Очистить().

Для Каждого СтрокаТовары Из ДокументОбъект.Товары Цикл

СтрокаТовары.Цена = СтрокаТовары.Цена * 1.1;

КонецЦикла;

НоваяСтрока = ДокументОбъект.Товары.Добавить();

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

НоваяСтрока.Количество = 1;

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

Типичные ошибки
01
Изменили цену в строке, но не пересчитали сумму и сумму НДС — документ записался с расхождениями, которые вылезут в отчётах
02
Присвоили значения реквизитам, но забыли вызвать Записать() — изменения потеряны при завершении кода
03
Изменили объект, полученный ранее, после того как документ успели отредактировать в интерфейсе — запись завершилась ошибкой конфликта версий

Запись, проведение и отмена проведения

Метод Записать() фиксирует изменения в базе. Режим записи определяет, что произойдёт с движениями документа. Передавать режим нужно явно, чтобы не полагаться на поведение по умолчанию.

Режим записиЧто происходитКогда применять
РежимЗаписиДокумента.ЗаписьСохраняются только данные документа, движения не формируютсяКорректировка непроведённого документа или реквизитов, не влияющих на учёт
РежимЗаписиДокумента.ПроведениеДокумент записывается и проводится, формируются движения по регистрамИзменение проведённого документа с пересчётом учёта
РежимЗаписиДокумента.ОтменаПроведенияДвижения удаляются, документ остаётся в базеПеред массовой корректировкой или переносом документа
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);

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

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

Изменение документа в транзакции

Если обработка меняет несколько документов или один документ вместе с другими объектами (например, регистром сведений), все операции нужно объединять в транзакцию. Тогда при ошибке на любом шаге база вернётся в исходное состояние, а не останется наполовину изменённой.

НачатьТранзакцию();

Попытка

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

ДокументОбъект.Комментарий = "Скорректировано";

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

ЗафиксироватьТранзакцию();

Исключение

ОтменитьТранзакцию();

ЗаписьЖурналаРегистрации("ИзменениеДокумента",

УровеньЖурналаРегистрации.Ошибка, , ,

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

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

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

Блокировки и ошибка «конфликт блокировок»

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

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

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

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

Как выбрать действие

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

Ограничения и проверка результата

Программная запись подчиняется тем же правам доступа и механизмам ограничения, что и интерактивная: роли пользователя, RLS, даты запрета изменения данных, подписки на события. Код, выполняемый под обычным пользователем, не сможет записать документ, закрытый датой запрета. В серверных процедурах иногда используют привилегированный режим (УстановитьПривилегированныйРежим), но это снимает и проверки прав — применять его нужно осознанно.

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

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

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

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

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

Признак Проведен соответствует ожидаемому режиму записи

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

Документ остался непроведённым, хотя записывался с режимом Проведение — значит, проведение завершилось ошибкой

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

Движения сформированы и отражают новые данные документа

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

Движения пустые или содержат старые суммы

Итоги отчётов по затронутым регистрам
Норма

Обороты и остатки изменились ожидаемым образом

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

Итоги не изменились или появились отрицательные остатки

Журнал регистрации
Норма

Нет ошибок за период работы обработки

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

Присутствуют ошибки записи или конфликтов блокировок

Часто задаваемые вопросы

Можно ли изменить документ без получения объекта?

Нет, изменение данных возможно только через ДокументОбъект. Через ссылку доступно лишь чтение реквизитов. Альтернатива — запрос с конструкцией ИЗМЕНИТЬ недоступна во встроенном языке: запросы в 1С только читают данные.

Как изменить помеченный на удаление документ?

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

Почему при программной записи не срабатывают проверки заполнения?

Часть проверок в типовых конфигурациях реализована в обработчиках формы, а они при программной записи не выполняются. Проверки в модуле объекта (ОбработкаПроверкиЗаполнения, ОбработкаПроведения) срабатывают. Если записать объект с ОбменДанными.Загрузка = Истина, отключаются и они.

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

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

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

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

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

Сам режим лишь отключает проверку прав и RLS, логика проведения продолжает работать. Риск в том, что код сможет изменить документы, недоступные пользователю, включая закрытые периоды. Применяйте привилегированный режим только там, где это обосновано архитектурой решения.

Итог

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

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