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