Как правильно установить значение реквизита при создании и изменении объекта в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Выбор подходящего обработчика события: ПередЗаписью против ПриЗаписи

Первый вопрос, который возникает у разработчика: в каком обработчике модуля объекта лучше всего прописывать логику установки флага? Рассмотрим два основных варианта: ПередЗаписью и ПриЗаписи.

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

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

Реализация базовой логики в модуле объекта

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


Процедура ПередЗаписью(Отказ)
    
    // 1. Проверка режима загрузки данных
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // 2. Установка флага при создании или изменении
    Если ЭтоНовый() Или Модифицированность() Тогда
        ЭтотОбъект.Новая = Истина;
    КонецЕсли;

КонецПроцедуры

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

Критически важный нюанс: Свойство ОбменДанными.Загрузка

Проанализируем пункт №1 в нашем примере кода. Использование проверки Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли; является обязательным стандартом разработки на 1С. Почему это важно? Представьте, что ваша база участвует в обмене. Когда данные приходят из другой базы (например, через РИБ или универсальный обмен), объект записывается программно в режиме загрузки. Если не поставить эту проверку, флаг Новая взведется даже при автоматическом обновлении данных из другого источника. Это может привести к «зацикливанию» обмена: база А передает данные в базу Б, база Б ставит флаг «изменено» и пытается отправить этот же объект обратно в базу А.

Сравнение с предыдущим состоянием для исключения лишних срабатываний

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

Рассмотрим более продвинутый пример кода:


Процедура ПередЗаписью(Отказ)

    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Если объект новый — сразу ставим флаг
    Если ЭтоНовый() Тогда
        ЭтотОбъект.Новая = Истина;
        Возврат;
    КонецЕсли;

    // Для существующего объекта проверим, изменились ли ключевые поля
    // Получаем текущие данные из базы данных по ссылке
    СтарыеДанные = Ссылка.ПолучитьОбъект();
    
    Если СтарыеДанные.Наименование <> ЭтотОбъект.Наименование 
        Или СтарыеДанные.Артикул <> ЭтотОбъект.Артикул 
        Или СтарыеДанные.ПометкаУдаления <> ЭтотОбъект.ПометкаУдаления Тогда
        
        ЭтотОбъект.Новая = Истина;
        
    КонецЕсли;

КонецПроцедуры

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

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

Если мы планируем в дальнейшем искать объекты с установленным флагом Новая для их выгрузки (например, с помощью запроса), нам необходимо позаботиться о производительности. Посмотрим на ситуацию со стороны СУБД: если в справочнике 100 000 элементов, а нам нужно найти всего 10 измененных, без специальной настройки серверу придется просканировать всю таблицу целиком (Full Scan).

Чтобы этого избежать, выполните следующие действия в Конфигураторе:

  1. Найдите ваш реквизит Новая в структуре справочника.
  2. Откройте его свойства.
  3. В поле «Индексировать» установите значение «Индексировать» или «Индексировать с доп. упорядочиванием».

Это создаст в базе данных индекс, который позволит запросам по этому полю отрабатывать мгновенно.

Реализация через расширение конфигурации

Если вы работаете с типовой конфигурацией (например, 1С:Бухгалтерия или 1С:Управление торговлей), крайне не рекомендуется вносить изменения напрямую в основную конфигурацию. Это усложнит дальнейшие обновления. Вместо этого воспользуемся механизмом Расширений.

Разберем шаги по созданию расширения:

  1. Создайте новое расширение или откройте существующее.
  2. Добавьте в расширение нужный справочник и его реквизит.
  3. Добавьте в расширение процедуру ПередЗаписью модуля объекта с аннотацией &Перед.

Код в расширении будет выглядеть так:


&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    Если ЭтоНовый() Или Модифицированность() Тогда
        ЭтотОбъект.Новая = Истина;
    КонецЕсли;
КонецПроцедуры

Нюанс: Групповое изменение реквизитов

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

Альтернатива: Планы обмена

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

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

← На главную