Как программно определить, какие реквизиты были изменены перед записью объекта в 1С 8.3?

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

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

Зачем отслеживать изменения реквизитов?

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

Основной метод: Сравнение текущего объекта с данными в базе

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

  1. Текущее состояние (в памяти): это данные, которые содержатся в свойствах ЭтотОбъект.
  2. Предыдущее состояние (в базе): это те данные, которые еще хранятся на диске. Доступ к ним можно получить по ссылке ЭтотОбъект.Ссылка.

Важно помнить, что если объект новый (ЭтоНовый() = Истина), то сравнивать его не с чем — все его заполненные реквизиты считаются измененными (созданными).

Пошаговая реализация сравнения реквизитов

Проанализируем алгоритм, который позволяет универсально обойти все реквизиты объекта и найти отличия. Разместим этот код в модуле объекта в процедуре ПередЗаписью. При работе со сложными структурами данных разработчикам может пригодиться MCP сервер для работы с метаданными 1С, упрощающий получение описаний объектов.

Разберем по шагам:

  1. Проверяем, является ли объект новым.
  2. Получаем данные объекта непосредственно из базы данных, используя метод ПолучитьОбъект() по ссылке.
  3. Используя метаданные, обходим коллекцию реквизитов.
  4. Сравниваем значения каждого реквизита в памяти и в полученном из базы объекте.

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Если объект новый, отслеживать изменения нет смысла
    Если ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;

    // Получаем состояние объекта из базы данных
    ОбъектИзБазы = Ссылка.ПолучитьОбъект();
    
    // Получаем метаданные текущего объекта (Справочник или Документ)
    МетаданныеОбъекта = Метаданные();
    
    СписокИзменений = Новый СписокЗначений;

    // Проходим циклом по всем основным реквизитам
    Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
        ИмяРеквизита = Реквизит.Имя;
        
        // Сравниваем значения. 
        // Внимание: для некоторых типов данных (например, ХранилищеЗначения) 
        // прямое сравнение может работать некорректно
        Если ЭтотОбъект[ИмяРеквизита] <> ОбъектИзБазы[ИмяРеквизита] Тогда
            СписокИзменений.Добавить(ИмяРеквизита, "Изменено: " + ИмяРеквизита);
            
            // Здесь можно сразу реализовать логику, например:
            // Если ИмяРеквизита = "СуммаДокумента" Тогда...
        КонецЕсли;
    КонецЦикла;

    // Выведем результат в окно сообщений для теста
    Для Каждого Изменение Из СписокИзменений Цикл
        Сообщить(Изменение.Представление);
    КонецЦикла;

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

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

Метод Ссылка.ПолучитьОбъект() является достаточно "тяжелым", так как он считывает из базы данных абсолютно все поля объекта, включая табличные части, даже если они нам не нужны. Если в объекте много данных (например, огромная табличная часть "Товары"), это может замедлить работу системы.

Проанализируем более эффективный способ — использование запроса. Мы выберем только те поля, которые нам действительно интересны для сравнения.


// Пример оптимизированного получения данных через запрос
ТекстЗапроса = "ВЫБРАТЬ
               |    Ссылка.Статус КАК Статус,
               |    Ссылка.Сумма КАК Сумма
               |ИЗ
               |    Документ.ЗаказПокупателя КАК Ссылка
               |ГДЕ
               |    Ссылка = &Ссылка";

Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    Если Выборка.Статус <> ЭтотОбъект.Статус Тогда
        // Статус изменился, выполняем действия
    КонецЕсли;
КонецЕсли;

Работа с табличными частями

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

Рассмотрим алгоритм сравнения табличных частей пошагово:

  1. Выгружаем табличную часть объекта в ТаблицаЗначений.
  2. Выгружаем табличную часть из ссылки (из базы) в другую ТаблицаЗначений.
  3. Используем метод Количество() для быстрой проверки на добавление/удаление.
  4. Если количество совпадает, используем метод Свернуть() или посимвольное сравнение строк для поиска отличий.

Совет: Для точного сравнения строк табличной части часто используют сравнение по номеру строки или по уникальному идентификатору ключа связи, если он предусмотрен в конфигурации.

Использование ДополнительныеСвойства для передачи информации

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

Рассмотрим пример передачи флага изменения:


// В процедуре ПередЗаписью
Если ЭтотОбъект.Сумма <> Ссылка.Сумма Тогда
    ЭтотОбъект.ДополнительныеСвойства.Вставить("СуммаИзменилась", Истина);
КонецЕсли;

// В процедуре ПриЗаписи
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("СуммаИзменилась") Тогда
    // Выполняем действия, зная, что сумма была изменена пользователем
КонецЕсли;

Важные замечания и ограничения

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

Таким образом, мы проанализировали несколько способов определения измененных реквизитов: от универсального перебора метаданных до точечных запросов. Выбор метода зависит от ваших требований к производительности, сложности структуры самого объекта и необходимости формировать анализ изменений конфигурации для отчетности или согласования с заказчиком.

← На главную