В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача отслеживания изменений в справочниках или документах для последующей выгрузки этих данных во внешние системы (сайты, мобильные приложения, другие базы данных). Одним из самых простых и наглядных способов является создание специального реквизита, например, Новая или Изменена с типом Булево. В данной статье мы подробно разберем, как реализовать автоматическую установку этого флага так, чтобы система работала корректно, быстро и не вызывала бесконечных циклов записи.
Первый вопрос, который возникает у разработчика: в каком обработчике модуля объекта лучше всего прописывать логику установки флага? Рассмотрим два основных варианта: ПередЗаписью и ПриЗаписи.
Проанализируем ситуацию. Если мы решим устанавливать значение реквизита в процедуре ПриЗаписи, мы столкнемся с серьезной проблемой. На момент выполнения этого обработчика данные уже физически записаны в таблицу базы данных. Если мы программно изменим значение реквизита Новая в этот момент, нам придется снова вызывать метод Записать(), чтобы сохранить это изменение. Это неминуемо приведет к рекурсии (процедура вызовет сама себя), что либо вызовет ошибку переполнения стека, либо потребует написания сложных проверок для исключения повторного входа.
Поэтому мы рекомендуем использовать предопределенную процедуру ПередЗаписью. Давайте выясним причину: в этом обработчике объект уже подготовлен к записи, но транзакция еще не завершена, и данные в БД еще не зафиксированы. Изменяя значение реквизита через ЭтотОбъект.Новая = Истина;, мы просто меняем данные в оперативной памяти. Платформа запишет их в базу данных за один проход вместе со всеми остальными изменениями пользователя. Это максимально производительный и безопасный путь.
Рассмотрим пример кода, который необходимо разместить в модуле справочника (например, Номенклатура). Нам нужно, чтобы флаг устанавливался в Истина при создании нового элемента или при изменении существующего.
Процедура ПередЗаписью(Отказ)
// 1. Проверка режима загрузки данных
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// 2. Установка флага при создании или изменении
Если ЭтоНовый() Или Модифицированность() Тогда
ЭтотОбъект.Новая = Истина;
КонецЕсли;
КонецПроцедуры
Разберем, что происходит в этом фрагменте кода. Функция ЭтоНовый() возвращает Истина, если объект еще ни разу не записывался в базу данных. Свойство Модифицированность() позволяет определить, были ли изменены какие-либо реквизиты объекта в форме перед нажатием кнопки «Записать».
Проанализируем пункт №1 в нашем примере кода. Использование проверки Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли; является обязательным стандартом разработки на 1С. Почему это важно? Представьте, что ваша база участвует в обмене. Когда данные приходят из другой базы (например, через РИБ или универсальный обмен), объект записывается программно в режиме загрузки. Если не поставить эту проверку, флаг Новая взведется даже при автоматическом обновлении данных из другого источника. Это может привести к «зацикливанию» обмена: база А передает данные в базу Б, база Б ставит флаг «изменено» и пытается отправить этот же объект обратно в базу А.
Иногда свойство Модифицированность() может давать ложноположительные результаты. Например, если пользователь просто открыл форму, случайно изменил символ и тут же его стер — объект будет считаться модифицированным, хотя по факту данные не изменились. Чтобы избежать лишних срабатываний флага (и, как следствие, лишней нагрузки на систему интеграции), мы можем применить метод сравнения с текущим состоянием базы данных.
Рассмотрим более продвинутый пример кода:
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Если объект новый — сразу ставим флаг
Если ЭтоНовый() Тогда
ЭтотОбъект.Новая = Истина;
Возврат;
КонецЕсли;
// Для существующего объекта проверим, изменились ли ключевые поля
// Получаем текущие данные из базы данных по ссылке
СтарыеДанные = Ссылка.ПолучитьОбъект();
Если СтарыеДанные.Наименование <> ЭтотОбъект.Наименование
Или СтарыеДанные.Артикул <> ЭтотОбъект.Артикул
Или СтарыеДанные.ПометкаУдаления <> ЭтотОбъект.ПометкаУдаления Тогда
ЭтотОбъект.Новая = Истина;
КонецЕсли;
КонецПроцедуры
В данном примере мы используем метод Ссылка.ПолучитьОбъект(), чтобы прочитать состояние объекта до момента текущей записи. Помните, что обращение к базе данных — операция ресурсоемкая, поэтому такое детальное сравнение стоит применять только для действительно важных полей, изменения которых требуют выгрузки.
Если мы планируем в дальнейшем искать объекты с установленным флагом Новая для их выгрузки (например, с помощью запроса), нам необходимо позаботиться о производительности. Посмотрим на ситуацию со стороны СУБД: если в справочнике 100 000 элементов, а нам нужно найти всего 10 измененных, без специальной настройки серверу придется просканировать всю таблицу целиком (Full Scan).
Чтобы этого избежать, выполните следующие действия в Конфигураторе:
Новая в структуре справочника.Это создаст в базе данных индекс, который позволит запросам по этому полю отрабатывать мгновенно.
Если вы работаете с типовой конфигурацией (например, 1С:Бухгалтерия или 1С:Управление торговлей), крайне не рекомендуется вносить изменения напрямую в основную конфигурацию. Это усложнит дальнейшие обновления. Вместо этого воспользуемся механизмом Расширений.
Разберем шаги по созданию расширения:
ПередЗаписью модуля объекта с аннотацией &Перед.Код в расширении будет выглядеть так:
&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если ЭтоНовый() Или Модифицированность() Тогда
ЭтотОбъект.Новая = Истина;
КонецЕсли;
КонецПроцедуры
Важно помнить, что стандартные средства системы, такие как Групповое изменение реквизитов, могут не вызывать логику модуля объекта, если не установлена галочка «Выполнять в транзакции» или «Записывать объекты с проверками». Если ваша система предполагает частое использование групповых правок, обязательно проинструктируйте пользователей или администраторов о необходимости включения этих опций, иначе флаги изменений не будут проставлены автоматически.
Хотя создание булевского реквизита — это рабочее решение, выясним, нет ли более профессионального подхода. Платформа 1С содержит встроенный механизм — Планы обмена. Вместо добавления реквизита в каждый справочник, вы можете создать план обмена и включить в него нужные объекты. Система сама будет регистрировать изменения (создавать записи в специальных таблицах регистрации), а после выгрузки вы сможете очистить эти записи одной командой. Это более масштабируемый подход, так как он не увеличивает размер основной таблицы справочника и не требует индексации булевских полей.
Однако, если ваша задача проста и вы ограничены в ресурсах на разработку сложной системы обмена, использование реквизита и обработчика ПередЗаписью, которое мы подробно разобрали, является вполне допустимым и надежным методом.