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