Почему не меняется договор в документе 1С при программной записи?

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

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

Разбор типичной ошибки: Ссылка vs Объект

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


Для каждого СтрокаИзВыборки Из ВыборкаДетальныеЗаписи Цикл
    // ОШИБКА: Изменение реквизита в ссылке или строке выборки ничего не дает
    СтрокаИзВыборки.ДоговорКонтрагента = НовыйДоговор; 
    
    ДокОбъект = СтрокаИзВыборки.Ссылка.ПолучитьОбъект();
    // В этот момент ДокОбъект прочитан из базы, и в нем старый договор!
    ДокОбъект.Записать(); 
КонецЦикла;

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

Решение проблемы: Изменение табличной части

Даже если вы правильно получили объект и изменили реквизит в шапке, данные могут «откатиться» при записи или открытии формы. В документах типа ПлатежноеПоручениеВходящее (и многих других в конфигурациях УПП, КА или БП 2.0) договор дублируется. Он есть и в шапке документа, и в табличной части РасшифровкаПлатежа.

Рассмотрим правильный алгоритм действий:

  1. Получаем объект документа через ссылку.
  2. Устанавливаем новый договор в реквизит шапки.
  3. Организуем цикл по строкам табличной части РасшифровкаПлатежа и меняем договор в каждой строке.
  4. Записываем объект.

Посмотрим на пример корректного кода, который решает задачу:


НовДоговор = Справочники.ДоговорыКонтрагентов.НайтиПоКоду("УТ0002294");

Для каждого СтрокаВыборки Из ВыборкаДетальныеЗаписи Цикл
    ДокОбъект = СтрокаВыборки.Ссылка.ПолучитьОбъект();
    
    // 1. Меняем в шапке
    ДокОбъект.ДоговорКонтрагента = НовДоговор;
    
    // 2. Обязательно меняем во всех строках расшифровки платежа
    Для каждого СтрокаТЧ Из ДокОбъект.РасшифровкаПлатежа Цикл
        СтрокаТЧ.ДоговорКонтрагента = НовДоговор;
    КонецЦикла;
    
    // 3. Записываем изменения
    Попытка
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Документ " + ДокОбъект + " успешно обновлен.");
    Исключение
        Сообщить("Не удалось записать документ: " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

Почему данные в форме могут выглядеть старыми?

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

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

Дополнительные технические нюансы

При массовом изменении документов следует учитывать еще несколько критически важных моментов:

1. Проведение документа и регистры. Если документ уже был проведен, простая запись (РежимЗаписиДокумента.Запись) изменит данные в самом документе, но не обновит движения по регистрам. В отчетах по взаиморасчетам вы по-прежнему будете видеть старый договор. Чтобы данные обновились в учете, используйте:

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

2. ОбменДанными.Загрузка. Если вам нужно обойти проверки и программные подмены в модуле объекта, используйте свойство ОбменДанными.Загрузка:


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

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

3. Соответствие валют. Проверьте, совпадает ли валюта нового договора с валютой документа. В типовых конфигурациях 1С запрещено использовать договор в валюте, отличной от валюты платежного документа (если это не договор в у.е.). Если валюты не совпадут, стандартные механизмы формы могут очистить поле договора сразу после открытия.

Оптимизация производительности

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

Использование ПолучитьОбъект() — тяжелая операция. Если вам нужно изменить тысячи документов, убедитесь, что ваш запрос максимально фильтрует выборку (через ГДЕ), чтобы не обрабатывать лишние данные. Проверяйте, не установлен ли уже нужный договор в документе, прежде чем получать его объект и пытаться записать.

← На главную