Почему метод ИсторияДанных.ВыбратьВерсии возвращает пустую таблицу и как правильно получить историю изменений?

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

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

Разбираемся в причинах: два механизма версионирования

Первое и самое важное, что нам необходимо проанализировать, — это то, какой именно механизм версионирования используется в вашей конфигурации. В современных решениях 1С (таких как УТ 11, БП 3.0, ЗУП 3.1) могут сосуществовать или заменять друг друга две технологии:

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

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

Проверяем настройки метаданных

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

Во-первых, выберем нужный объект (например, Документ) и в палитре свойств найдем параметр История данных. Он должен быть установлен в значение Использовать.

Во-вторых, проверим права доступа. У пользователя в его роли должны быть установлены права Чтение истории данных и Просмотр истории данных. Даже если у пользователя полные права на сам объект, отсутствие специфических прав на историю приведет к получению пустого результата.

В-третьих, проанализируем настройки состава истории. Если для конкретных реквизитов объекта история отключена, то при их изменении новые версии создаваться не будут.

Правильное использование метода ОбновитьИсторию

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

Для принудительного обновления истории из кода необходимо вызвать метод ОбновитьИсторию. Посмотрим на правильный синтаксис, так как часто возникают ошибки в передаваемых параметрах:


// Передаем непосредственно ссылку на объект для обновления
ИсторияДанных.ОбновитьИсторию(ОбъектСсылка);

// Теперь подготавливаем отбор для выборки
Отбор = Новый Структура;
Отбор.Вставить("Данные", ОбъектСсылка);

// Выполняем выборку версий
Версии = ИсторияДанных.ВыбратьВерсии(Отбор);

Обратите внимание, что в методе ОбновитьИсторию передается сама ссылка, а в методе ВыбратьВерсии — структура с ключом "Данные".

Тонкая настройка отбора версий

Рассмотрим подробнее структуру отбора. Платформа позволяет гибко фильтровать данные. Например, нам могут понадобиться версии только за определенный период или только те, где изменились конкретные поля. Проанализируем пример сложного отбора:


Отбор = Новый Структура;
Отбор.Вставить("Данные", ДокументСсылка);

// Добавим фильтр по конкретному реквизиту
СписокПолей = Новый Массив;
СписокПолей.Добавить("СуммаДокумента");
СписокПолей.Добавить("Контрагент");
Отбор.Вставить("ИзменениеЗначенийПолей", СписокПолей);

// Получаем версии, где менялись только указанные поля
ВыборкаВерсий = ИсторияДанных.ВыбратьВерсии(Отбор);

Если в вашей конфигурации метод все равно возвращает пустоту, попробуйте очистить и заново построить индекс полнотекстового поиска и индекс истории данных. Иногда в старых релизах платформы (например, 8.3.16) наблюдались сбои синхронизации индексов.

Как получить сами данные версии?

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


Для Каждого СтрокаВерсии Из ВыборкаВерсий Цикл
    
    // Получаем структуру данных конкретной версии
    ДанныеВерсии = ИсторияДанных.ПолучитьДанныеВерсии(СтрокаВерсии.Данные, СтрокаВерсии.НомерВерсии);
    
    // ДанныеВерсии — это Структура, где ключи — имена реквизитов
    Если ДанныеВерсии.Свойство("СуммаДокумента") Тогда
        Сообщить("В версии №" + СтрокаВерсии.НомерВерсии + " сумма была: " + ДанныеВерсии.СуммаДокумента);
    КонецЕсли;
    
    // Работа с табличными частями
    Если ДанныеВерсии.Свойство("Товары") Тогда
        Для Каждого СтрокаТЧ Из ДанныеВерсии.Товары Цикл
            // СтрокаТЧ также является структурой
            Сообщить("Товар: " + СтрокаТЧ.Номенклатура + ", Количество: " + СтрокаТЧ.Количество);
        КонецЦикла;
    КонецЕсли;
    
КонецЦикла;

Что делать, если используется механизм БСП (регистр ВерсииОбъектов)?

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


// В конфигурациях на базе БСП обычно используется общий модуль ВерсионированиеОбъектов
// Получим список всех версий объекта через запрос к регистру
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВерсииОбъектов.НомерВерсии КАК НомерВерсии,
    |	ВерсииОбъектов.ДатаВерсии КАК ДатаВерсии
    |ИЗ
    |	РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
    |ГДЕ
    |	ВерсииОбъектов.Объект = &Объект
    |УПОРЯДОЧИТЬ ПО
    |	НомерВерсии УБЫВ";

Запрос.УстановитьПараметр("Объект", ДокументСсылка);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Для получения данных версии используем метод БСП
    ДанныеВерсии = ВерсионированиеОбъектов.ПолучитьВерсиюОбъекта(ДокументСсылка, Выборка.НомерВерсии);
    // На выходе получаем объект (например, ДокументОбъект), заполненный данными из версии
    Сообщить("Дата версии: " + Выборка.ДатаВерсии + " Сумма: " + ДанныеВерсии.СуммаДокумента);
КонецЦикла;

Резюме и рекомендации

Проанализировав все возможные ситуации, мы можем составить алгоритм действий при возникновении проблем с ИсторияДанных.ВыбратьВерсии:

  1. Проверьте тип версионирования. Если в типовой конфигурации включено «Версионирование объектов» через настройки программы, скорее всего, вам нужен регистр ВерсииОбъектов.
  2. Соблюдайте последовательность вызовов. Всегда вызывайте ИсторияДанных.ОбновитьИсторию(Ссылка) перед выборкой, чтобы исключить влияние задержек очереди фоновых заданий.
  3. Контролируйте права доступа. Проверьте роли пользователя на наличие прав Чтение истории данных.
  4. Используйте правильный синтаксис отбора. Ключ в структуре должен называться именно "Данные", а значением должна выступать ссылка на объект.

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

← На главную