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

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

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

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

Особенности хранения данных версионирования в 1С

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

  1. Измерения регистра: Объект (ссылка на элемент справочника или документ) и НомерВерсии (число).
  2. Реквизиты: ДатаВерсии, АвторВерсии и служебные поля.
  3. Ресурс: ВерсияОбъекта с типом ХранилищеЗначения.

Внутри ресурса ВерсияОбъекта данные сериализуются в формат FastInfoset (сжатое бинарное XML-представление объекта целиком). Пример того, как платформа упаковывает объект в хранилище:


Функция СериализоватьОбъект(Объект) Экспорт
    ЗаписьXML = Новый ЗаписьFastInfoset;
    ЗаписьXML.УстановитьДвоичныеДанные();
    ЗаписьXML.ЗаписатьОбъявлениеXML();
    
    ЗаписатьXML(ЗаписьXML, Объект, НазначениеТипаXML.Явное);
    
    Возврат ЗаписьXML.Закрыть();
КонецФункции

Поскольку конкретные реквизиты (например, Ответственный или Менеджер) не выделены в отдельные измерения или реквизиты регистра, а упакованы в бинарный поток, получить значение реквизита прямым запросом к регистру невозможно. Язык запросов 1С не умеет разбирать сериализованный XML внутри ХранилищеЗначения.

Программный способ: выборка и десериализация версий

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

Рассмотрим по шагам реализацию этого механизма.

Шаг 1. Получение списка версий за период

Сформируем запрос к регистру сведений ВерсииОбъектов для получения записей по нужным объектам за интересующий интервал дат:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВерсииОбъектов.Объект КАК Объект,
    |    ВерсииОбъектов.НомерВерсии КАК НомерВерсии,
    |    ВерсииОбъектов.ВерсияОбъекта КАК ВерсияОбъекта,
    |    ВерсииОбъектов.ДатаВерсии КАК ДатаВерсии
    |ИЗ
    |    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
    |ГДЕ
    |    ВерсииОбъектов.ДатаВерсии МЕЖДУ &ДатаС И &ДатаПо
    |    И ВерсииОбъектов.Объект ССЫЛКА Справочник.ДоговорыКонтрагентов
    |УПОРЯДОЧИТЬ ПО
    |    ВерсииОбъектов.Объект,
    |    ВерсииОбъектов.ДатаВерсии";

Запрос.УстановитьПараметр("ДатаС", НачалоПериода);
Запрос.УстановитьПараметр("ДатаПо", КонецДня(КонецПериода));
Выборка = Запрос.Выполнить().Выбрать();

Шаг 2. Разбор версии с использованием БСП

Для распаковки бинарных данных задействуем типовую функцию подсистемы версионирования БСП ВерсионированиеОбъектов.РазборВерсии:


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

Шаг 3. Построение интервалов действия менеджеров

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

  1. Определение начального менеджера: Для каждого договора необходимо получить срез на начало месяца — взять версию с максимальной датой, предшествующей началу периода. Если таких версий нет, берется первая историческая версия либо текущее значение из самого объекта.
  2. Фиксация точек изменения: Каждая смена ответственного внутри месяца завершает предыдущий интервал ДатаС - ДатаПо и открывает новый.
  3. Связывание с взаиморасчетами: Итоговая таблица интервалов передается во временную таблицу и соединяется с регистром взаиморасчетов по условию: Движения.Договор = Интервалы.Договор И Движения.Период МЕЖДУ Интервалы.ДатаС И Интервалы.ДатаПо.

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

Разберем важные архитектурные риски данного подхода:

  1. Производительность: Десериализация XML в цикле выполняется на сервере последовательно. При тысячах договоров это приведет к зависанию отчета и избыточному потреблению оперативной памяти.
  2. Неполнота истории: Регламентная очистка устаревших версий или программная запись объектов с отключенным версионированием приводят к пробелам в данных.
  3. Назначение подсистемы: Версионирование в БСП спроектировано для ручного аудита изменений пользователями, а не для регулярных аналитических отчетов.

Альтернативные решения без изменения конфигурации поставщика

Если требуется надежное и быстрое решение, рассмотрим следующие варианты:

  1. Использование расширения конфигурации (CFE): Создаем в расширении периодический регистр сведений (периодичность — День или Позиция регистратора) ИсторияОтветственныхПоДоговорам. Через подписку на событие ПриЗаписи фиксируем изменения менеджера. Это позволит строить отчет обычным быстрым запросом к виртуальной таблице СрезПоследних.
  2. Платформенная «История данных»: Если используется встроенный механизм платформы 8.3 (а не БСП), данные истории можно получать через системный менеджер истории данных или конструкцию ИСТОРИЯДАННЫХ языка запросов.
← На главную