Почему не устанавливается признак модифицированности для набора записей регистра бухгалтерии при программном изменении?

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

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

Анализ проблемы: почему Модифицированность() возвращает Ложь?

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


пПроводки = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
пПроводки.Отбор.Регистратор.Установить(ДокументСсылка);
пПроводки.Прочитать();

Для Каждого пПроводка Из пПроводки Цикл
    // Предположим, здесь есть логика проверки
    пПроводка.СуммаНУДт = 0;
    пПроводка.СуммаПРДт = 0;
    // ... изменение других ресурсов
КонецЦикла;

Если пПроводки.Модифицированность() Тогда 
    пПроводки.Записать();
КонецЕсли;

Несмотря на то что данные внутри коллекции пПроводки действительно изменились (в чем можно убедиться в отладчике), метод Модифицированность() упорно возвращает значение Ложь. Проанализируем причину такого поведения платформы.

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

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

Решение №1: Использование собственного флага модифицированности

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

Выясним, как интегрировать свой флаг в алгоритм:


пПроводки = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
пПроводки.Отбор.Регистратор.Установить(ДокументСсылка);
пПроводки.Прочитать();

ЛокальнаяМодифицированность = Ложь;

Для Каждого пПроводка Из пПроводки Цикл
    
    // Проверяем, нужно ли реально менять значение, чтобы не взводить флаг зря
    Если пПроводка.СуммаНУДт <> 0 Тогда
        пПроводка.СуммаНУДт = 0;
        ЛокальнаяМодифицированность = Истина;
    КонецЕсли;
    
    // Аналогично для других ресурсов
    Если пПроводка.СуммаПРДт <> 0 Тогда
        пПроводка.СуммаПРДт = 0;
        ЛокальнаяМодифицированность = Истина;
    КонецЕсли;
    
КонецЦикла;

// Используем свой флаг вместо системного метода
Если ЛокальнаяМодифицированность Тогда 
    пПроводки.Записать();
КонецЕсли;

Этот подход является наиболее «чистым» с точки зрения производительности. Мы вызываем Записать() только тогда, когда в данных действительно произошли изменения.

Решение №2: Безусловная запись набора

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

Проанализируем этот вариант:

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

Особенности для разных типов регистров

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

Разберем еще один важный момент: если вы используете НаборЗаписей в форме документа (например, вывели движения на отдельную закладку), то при изменении данных в коде формы флаг модифицированности самой Формы может быть установлен (через свойство ЭтотОбъект.Модифицированность), но системный флаг самого программного объекта набора все равно может остаться в значении Ложь до момента интерактивного действия пользователя в строке.

Выводы и рекомендации

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

  1. Не используйте метод Модифицированность() для программно созданных наборов записей вне контекста форм, если вы только меняете значения полей в существующих строках.
  2. Реализуйте сравнение старого и нового значения внутри цикла, если вам критично избежать лишних операций записи в базу данных.
  3. Помните, что добавление (Добавить()) или удаление строк в наборе обычно корректно взводит системную модифицированность, но для надежности в сложных алгоритмах лучше всегда использовать явный флаг (Булево).
  4. При записи набора всегда учитывайте, что метод Записать() полностью замещает существующие в базе записи по данному регистратору (или набору измерений для независимых регистров) теми, что находятся в памяти набора.

Соблюдение этих простых правил поможет избежать трудноуловимых ошибок, когда данные в базе «почему-то не обновились», хотя код отработал без ошибок, но просто не прошел внутрь условия Если Модифицированность() Тогда.

← На главную