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

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

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

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

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

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

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

Получение объекта документа для изменения

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

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

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

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

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

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

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

Реквизиты шапки изменяются обычным присваиванием: ДокументОбъект.Склад = НовыйСклад;. Типы должны совпадать — присвоение строки в реквизит типа «Число» вызовет ошибку преобразования. Для ссылочных реквизитов присваивайте именно ссылки, а не строковые представления.

Табличные части представляют собой коллекции строк. Типовые операции выглядят так:

// Изменить существующую строку

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

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

КонецЦикла;

// Добавить строку

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

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

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

// Удалить строку

ДокументОбъект.Товары.Удалить(0);

// Очистить всю табличную часть

ДокументОбъект.Товары.Очистить();

Обратите внимание: пересчёт сумм, налогов и итогов при программном изменении не выполняется автоматически — обработчики «ПриИзменении», которые срабатывают в форме, в объекте не вызываются. Если в модуле объекта такой пересчёт вынесен в экспортную процедуру, вызовите её явно после изменения строк, иначе сумма документа перестанет совпадать с суммами по строкам.

Типичные ошибки
01
Изменили цены в табличной части, но не пересчитали суммы — документ внутренне противоречив
02
Присвоили строку вместо ссылки в ссылочный реквизит — ошибка преобразования типов
03
Удалили строки в цикле по индексу с начала коллекции — индексы съехали, часть строк пропущена
04
Изменили объект, но забыли вызвать Записать() — изменения потеряны

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

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

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

Пример перепроведения после изменения:

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

ДокументОбъект.Организация = НоваяОрганизация;

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

Если документ не проводится из-за контроля остатков или заполненности, Записать(РежимЗаписиДокумента.Проведение) вызовет исключение — и данные шапки тоже не сохранятся. Когда нужно гарантированно сохранить изменения даже при невозможности проведения, сначала записывайте в режиме Запись, а проведение выполняйте отдельным вызовом внутри попытки.

Дополнительный параметр РежимПроведенияДокумента.Оперативный / Неоперативный влияет на контроль остатков для оперативных документов. Оперативное проведение возможно только «текущей датой»; при программной обработке старых документов используйте неоперативный режим.

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

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

bsl
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Документ.РеализацияТоваровУслуг ГДЕ Дата МЕЖДУ &Начало И &Конец");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	НачатьТранзакцию();
	Попытка
		Объект = Выборка.Ссылка.ПолучитьОбъект();
		Объект.Комментарий = "Обработано";
		Объект.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписьЖурналаРегистрации("ПакетнаяОбработка", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
	КонецПопытки;
КонецЦикла;

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

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

Изменение без нарушения обменов, прав и блокировок

При программной записи могут сработать подписки на события, регистрация изменений для обмена, бизнес-процессы и проверки прав. Часто это желательно, но в служебных сценариях (миграция, загрузка из внешнего источника) требуется записать данные «тихо». Для этого используется параметр ОбменДанными:

ДокументОбъект.ОбменДанными.Загрузка = Истина;

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

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

Учитывайте и права: код на сервере выполняется в контексте текущего пользователя, и Записать() завершится ошибкой «Нарушение прав доступа», если у него нет права изменения документа. В регламентных заданиях код обычно выполняется от имени служебного пользователя — проверьте его роли заранее. Для обхода проверок прав внутри транзакции можно использовать УстановитьПривилегированныйРежим(Истина), но делать это следует осознанно и только в серверных процедурах.

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

Как выбрать способ изменения

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

Проверка результата и контроль целостности

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

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

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

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

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

Свойство Проведен = Истина, движения присутствуют в регистрах

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

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

Сумма документа и суммы строк
Норма

Итог табличной части равен реквизиту СуммаДокумента

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

Суммы расходятся — не выполнен пересчёт

Журнал регистрации после пакетной обработки
Норма

Нет ошибок по обработанным ссылкам

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

Ошибки блокировок или прав — часть документов не обработана

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

Изменения зарегистрированы в планах обмена, если это требуется

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

При Загрузка = Истина изменения не ушли в другие базы

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

Чем отличается запись через ПолучитьОбъект() от изменения через форму?

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

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

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

Почему возникает ошибка «конфликт блокировок» и что делать?

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

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

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

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

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

Безопасно ли использовать ОбменДанными.Загрузка для обычных правок?

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

Итог

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

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