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