В процессе сопровождения баз 1С часто возникает задача массовой корректировки данных в уже проведенных документах. Одной из наиболее коварных операций является смена договора в документе Списание с расчетного счета. Начинающие разработчики часто сталкиваются с ситуацией, когда код вроде бы написан верно, отладчик показывает присвоение значения, но после записи документа изменения не сохраняются или «слетают». В этой статье мы подробно разберем, почему так происходит, проанализируем структуру документа и выработаем правильный алгоритм решения задачи.
Рассмотрим исходную ситуацию, с которой столкнулся автор темы. В его коде было допущено несколько критических ошибок, которые замедляли работу системы и приводили к неверному результату. Проанализируем фрагмент кода:
Пока ВыборкаДокументов.Следующий() Цикл
Док = ВыборкаДокументов.Ссылка.ПолучитьОбъект();
Если ВыборкаДокументов.Ссылка.ПолучитьОбъект().Контрагент <> Неопределено Тогда
// ... логика изменения
КонецЕсли;
КонецЦикла;
Во-первых, обратим внимание на избыточное получение объекта. Метод ПолучитьОбъект() — это ресурсоемкая операция, которая считывает все данные документа из базы в оперативную память. В примере выше автор вызывает этот метод дважды для одного и того же документа. Выясним причину, почему это плохо: при обработке тысячи документов система выполнит две тысячи тяжелых запросов вместо одной тысячи. Правильно будет получить объект один раз и работать с ним через переменную.
Во-вторых, проверка на Неопределено для ссылочных типов (таких как Контрагент) не совсем корректна. В 1С ссылка не может быть Неопределено, она может быть либо пустой, либо указывать на конкретный элемент. Правильнее использовать метод ПустаяСсылка().
Главная причина, по которой программная замена договора в шапке документа Списание с расчетного счета часто не дает результата, кроется в архитектуре современных конфигураций (например, Бухгалтерия предприятия 3.0 или ERP). Проанализируем структуру данных этого документа.
В документе Списание с расчетного счета информация о платеже, включая договор, ставку НДС и счета расчетов, хранится не только (а часто и не столько) в реквизитах шапки, но и в табличной части РасшифровкаПлатежа. Даже если на форме документа вы видите только одно поле договора, под капотом система создает одну строку в этой табличной части.
Если мы программно меняем только Док.ДоговорКонтрагента в шапке, то при вызове метода Записать(РежимЗаписиДокумента.Проведение) срабатывают типовые механизмы конфигурации. Они видят, что в табличной части РасшифровкаПлатежа остался старый договор, и «синхронизируют» данные, перезаписывая ваше новое значение старым из таблицы. Именно поэтому в отладчике вы видите изменение, а в базе — нет.
Для того чтобы корректно изменить договор, нам необходимо обновить его во всех местах, где он упоминается. Рассмотрим оптимизированный и правильный код решения задачи:
// Предположим, ВыборкаДокументов уже сформирована
Пока ВыборкаДокументов.Следующий() Цикл
// Проверяем условие по ссылке, не получая объект целиком - это экономит ресурсы
Если ВыборкаДокументов.Контрагент = Справочники.Контрагенты.НайтиПоКоду("000006393") Тогда
ДокОбъект = ВыборкаДокументов.ПолучитьОбъект();
НовыйДоговор = Справочники.ДоговорыКонтрагентов.НайтиПоКоду("000008267");
// 1. Меняем договор в шапке
ДокОбъект.ДоговорКонтрагента = НовыйДоговор;
// 2. Обязательно меняем договор в табличной части РасшифровкаПлатежа
Для Каждого СтрокаТЧ Из ДокОбъект.РасшифровкаПлатежа Цикл
СтрокаТЧ.ДоговорКонтрагента = НовыйДоговор;
КонецЦикла;
// 3. Записываем и проводим документ
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Не удалось записать документ: " + ВыборкаДокументов.Ссылка + " Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Если вам нужно разово поменять договоры и вы не хотите писать код, рассмотрим стандартный инструмент 1С — обработку Групповое изменение реквизитов. Она доступна в разделе «Администрирование» — «Обслуживание» — «Корректировка данных».
Однако здесь есть нюанс. Если вы просто выберете документы и попытаетесь изменить реквизит Договор, вы можете столкнуться с той же проблемой — реквизитов может быть несколько. Разберем по шагам, как сделать это правильно:
Договор в обоих разделах и установить для них новое значение.При программной смене договора обязательно проанализируем ситуацию с Владельцем. Справочник ДоговорыКонтрагентов является подчиненным справочнику Контрагенты. Рассмотрим, что произойдет, если мы по ошибке подставим договор, принадлежащий другому контрагенту. В лучшем случае система выдаст ошибку при записи, в худшем — запишет некорректные данные, что приведет к «разрыву» аналитики в бухгалтерских отчетах.
Перед записью полезно добавить проверку:
Если НовыйДоговор.Владелец <> ДокОбъект.Контрагент Тогда
Сообщить("Ошибка: Выбранный договор не принадлежит контрагенту документа!");
Возврат;
КонецЕсли;
Также выясним причину возможного сброса договора из-за Вида операции. Если в документе выбран вид операции «Оплата поставщику», а вы пытаетесь подставить договор с видом «Прочее», стандартные проверки 1С могут очистить это поле при проведении. Всегда проверяйте соответствие Вида договора хозяйственной операции документа.
В завершение отметим, что если документ находится в закрытом периоде или по нему нельзя выполнять стандартные проверки, можно использовать свойство ОбменДанными.Загрузка = Истина. Это позволит записать объект «как есть», минуя большинство встроенных обработчиков. Однако помните: в этом случае движения по регистрам (проводки) могут не обновиться автоматически, и вам потребуется перепровести документ вручную или программно.