Как получить историю изменения основного менеджера в справочнике Партнеры через внешний отчет СКД?

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

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

Разбор основной проблемы: почему запрос возвращает неверные данные

Рассмотрим типичную ситуацию. Программист создает запрос к регистру ВерсииОбъектов и пытается вытащить данные через обращение к ссылке:


ВЫБРАТЬ
    ВерсииОбъектов.Объект.ОсновнойМенеджер КАК Менеджер,
    ВерсииОбъектов.НомерВерсии КАК Номер
ИЗ
    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
    ВерсииОбъектов.Объект = &Партнер

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

Выбор стратегии решения

Поскольку мы работаем в рамках внешнего отчета и не можем менять код общих модулей, нам необходимо реализовать алгоритм десериализации данных версии прямо в модуле отчета. Использование вычисляемых полей в СКД здесь малоэффективно, так как функция из общего модуля БСП ВерсионированиеОбъектов.ОтчетПоВерсииОбъекта возвращает табличный документ, предназначенный для визуального просмотра, а не для программной обработки.

Наиболее правильный подход — использование внешнего набора данных в СКД и программное заполнение таблицы значений в модуле отчета через процедуру ПриКомпоновкеРезультата.

Шаг 1: Подготовка схемы компоновки данных (СКД)

В схеме компоновки данных нашего отчета создадим Набор данных - объект. Назовем его, например, ТаблицаИстории. В этом наборе данных добавим следующие поля:

  1. Партнер (тип: СправочникСсылка.Партнеры)
  2. НомерВерсии (тип: Число)
  3. ДатаВерсии (тип: Дата)
  4. АвторВерсии (тип: СправочникСсылка.Пользователи)
  5. ОсновнойМенеджер (тип: СправочникСсылка.Пользователи)

Шаг 2: Программная реализация в модуле отчета

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

Рассмотрим пример кода для извлечения данных из версий:


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

Разберем по шагам ключевые моменты решения

Проанализируем, что мы сделали в данном коде:

  1. Отключение стандартной обработки: Установив СтандартнаяОбработка = Ложь, мы берем управление выводом отчета на себя. Это необходимо для работы с внешними источниками данных.
  2. Использование ОбъектИзХранилища: Это ключевой метод подсистемы версионирования БСП. Он читает XML-данные из ресурса ВерсияОбъекта и превращает их в объект 1С (обычно структуру или непосредственно объект справочника). Это позволяет нам обратиться к любому реквизиту (ОсновнойМенеджер, Наименование и т.д.) так, как будто мы работаем с живым объектом.
  3. Формирование структуры данных: Мы подготовили ТаблицуЗначений, чьи колонки в точности соответствуют именам полей в наборе данных "Объект" нашей СКД.

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

Рассмотрим ситуацию, когда версий очень много. Операция ОбъектИзХранилища достаточно тяжелая, так как включает в себя чтение XML и создание объектов в памяти. Если мы хотим вывести только те записи, где менеджер действительно изменился, нам нужно добавить проверку.

Посмотрим на пример логики сравнения:


ПредыдущийМенеджер = Неопределено;
// Предварительно нужно отсортировать выборку по Партнеру и Номеру версии
Пока Выборка.Следующий() Цикл
    ОбъектВерсии = ВерсионированиеОбъектов.ОбъектИзХранилища(Выборка.Партнер, Выборка.НомерВерсии);
    ТекущийМенеджер = ОбъектВерсии.ОсновнойМенеджер;
    
    Если ТекущийМенеджер <> ПредыдущийМенеджер Тогда
        // Записываем в таблицу только если значение изменилось по сравнению с прошлой версией
        // ... (код добавления строки)
        ПредыдущийМенеджер = ТекущийМенеджер;
    КонецЕсли;
КонецЦикла;

Важные замечания по типам данных

При работе с внешними наборами данных СКД крайне важно соблюдать строгое соответствие типов. Если в схеме СКД вы указали для поля ОсновнойМенеджер тип СправочникСсылка.Пользователи, то и в ТаблицеЗначений в коде 1С колонка должна быть создана с этим же типом. В противном случае СКД может вывести пустые значения или выдать ошибку о несовпадении типов.

Если в вашей конфигурации основной менеджер — это ФизическоеЛицо, обязательно проверьте это в дереве метаданных перед написанием кода. Также помните, что версии объектов могут храниться не для всех партнеров, а только для тех, у которых включено версионирование в настройках системы (Администрирование -> Общие настройки -> История изменений).

Таким образом, мы научились извлекать исторические данные из подсистемы версионирования БСП. Этот метод универсален и подходит для получения истории любых реквизитов (контактная информация, доп. реквизиты, склады и т.д.) в любых справочниках и документах системы.

← На главную