Глава 25. История данных
страницаГлава 25. История данных
25.1. Общая информация
История данных ‑ специальный механизм, который позволяет хранить в базе данных данные объектов конфигурации, упорядоченные по шкале времени. В качестве равноправного термина также будет использоваться термин версионирование данных. В базе данных хранится версия ‑ данные, которые были в объекте на момент изменения, а также состояние метаданных на момент изменения. Механизм позволяет настраивать хранение истории данных для объекта целиком и более тонко управлять работой механизма для каждого реквизита (включая табличные части). При версионировании данных предоставляется возможность добавлять к каждой версии произвольные данные, состав и наполнение которых определяется разработчиком. Механизм также позволяет выполнять действия, которые должны произойти после того, как версия данных будет фактически записана в таблицы базы данных (выполнять постобработку версий). Постобработка может быть использована в тех случаях, когда факт записи версии какого-либо объекта дает старт одному или нескольким действиям. Но принципиальным является то, что эти действия должны быть выполнены только после того, как успешно записан и сам объект и его очередная версия.
Управлять включением и выключением истории можно как из конфигуратора, так и с помощью встроенного языка. Таким образом, становится возможным включать хранение истории изменений только для тех данных, которые нужны конкретным пользователям, без необходимости изменения конфигурации.
Кроме хранения собственно изменений, механизм истории данных позволяет выполнять следующие операции:
● записывать версию данных для поддерживаемого объекта;
● записывать дополнительные данные, которые отсутствуют в фактических данных версионируемого объекта;
● получать данные определенной версии (включая дополнительные данные, если они заданы);
● удалять данные определенной версии;
● получать разницу между двумя элементами истории данных;
● а также другие полезные возможности.
История данных поддерживается для следующих объектов:
● общие реквизиты;
● константы;
● планы обмена;
● справочники;
● документы;
● планы видов характеристик;
● планы счетов;
● планы видов расчета;
● бизнес-процессы;
● задачи;
● регистры сведений.
Работа с историей данных регламентируется правами доступа и протоколируется в журнале регистрации.
История данных не переносится с помощью механизмов распределенной информационной базы.
25.2. Устройство механизма
История данных хранится в специальных таблицах той же информационной базы, для которой настраивается версионирование. Кроме собственно данных, история данных также хранит метаданные прежних версий версионируемых объектов. Версия метаданных для объекта создается в момент изменения метаданных объекта. Момент создания версии метаданных никак не связан с изменением данных версионируемого объекта. В истории данных не хранятся типы реквизитов, но хранятся только фактические значения этих реквизитов. Также следует помнить, что с точки зрения устройства системы, следующие пары действий являются одинаковыми:
● включение версионирования для реквизита и создание нового реквизита;
● отключение версионирования реквизита и удаление реквизита.
Кроме хранения измененных данных самого версионируемого объекта, механизм версионирования позволяет для каждой версии указать специальный комментарий и дополнительные данные, которые могут быть использованы при последующем просмотре и анализе измененных данных.
Процесс формирования версии состоит из нескольких шагов:
1. Система фиксирует факт изменения того или иного объекта. Это происходит или автоматически или с помощью вызова метода программного интерфейса. Информация об изменении объекта попадает в специальную очередь (очередь версий). В этот момент имеется возможность добавить к версионируемым данным объекта произвольные данные, которые требуются в дальнейшей работе (метод версионируемого объекта ЗаписьИсторииДанных.ДобавитьДополнительныеДанные()).
2. Выполняется перенос данных из очереди версий в таблицы базы данных, фактически хранящие данные версий. Эта операция выполняется при помощи метода ИсторияДанных.ОбновитьИсторию(). Обновление истории данных можно выполнять асинхронно, например, с помощью регламентного задания (по аналогии с обновлением индекса полнотекстового поиска). Другими словами, версия данных становится «настоящей» только после того, как она перенесена из очереди версий в таблицы хранения истории. При этом несколько подряд идущих изменений объекта данных (неважно, как эти изменения выполнены) фиксируются отдельно и переносятся также отдельно. Таким образом, в истории данных фиксируются все изменения объектов данных, вне зависимости от периодичности обновления истории и количества подряд идущих изменений одного объекта. Каждая обработанная версия данных будет перенесена в специальную очередь версий, требующих обработки после записи версий (очередь постобработки). Необходимость такого переноса определяется свойством версионируемого объекта ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписи.
3. После окончания обработки очереди версий, начинается обработка очереди постобработки. Эта обработка может быть выполнена как в рамках вызова метода ИсторияДанных.ОбновитьИсторию(), так и с помощью специального метода ИсторияДанных.ВыполнитьОбработкуПослеЗаписиВерсий(). Собственно обработка должна выполняется в обработчиках ОбработкаПослеЗаписиВерсийИсторииДанных() модулей менеджера версионируемых объектов.
4. После того, как постобработка версии данных завершена, эту версию необходимо явно удалить из очереди постобработки с помощью метода ИсторияДанных.УдалитьИзОбработкиПослеЗаписиВерсий(). Если этого не сделать, то эта версия будет постоянно передаваться в обработчики событий ОбработкаПослеЗаписиВерсийИсторииДанных().
5. Очередь постобработки можно принудительно очистить вызовом метода ИсторияДанных.УдалитьИзОбработкиПослеЗаписиВерсий().
Первые два шага вышеприведенного описания являются обязательными и выполняются всегда при формировании версии данных. Последующие шаги выполняются только в тех случаях, когда разработчик хочет использовать очередь постобработки.
Также следует помнить, что формирование истории данных можно условно разделить на два способа: обычный способ, который описан в данном разделе, и ускоренный способ. Во втором случае можно принудительно инициировать весь процесс обработки записи версии данных, но сделать это только для одной версии одного объекта данных. Более подробно этот способ обработки будет рассмотрен отдельно.
Настройка версионирования может осуществляться двумя способами:
1. в конфигураторе;
2. с помощью встроенного языка.
С помощью конфигуратора настраивать историю данных имеет смысл в том случае, когда разработчик прикладного решения точно уверен, что история данных для какого-либо объекта будет требоваться в любых вариантах работы системы или на эту историю завязана некоторая прикладная логика.
Если история данных используется для мониторинга работы с информационной базой и служит для ответа на вопрос «почему так?», то включение истории данных логично реализовать с помощью встроенного языка так, чтобы пользователь прикладного решения мог сам выбрать объекты и реквизиты, для которых нужно версионирование. Также следует помнить, что включение истории данных из встроенного языка не требует снятия конфигурации с поддержки.
Для того чтобы включить использование истории данных для объекта в конфигураторе, необходимо для этого объекта установить свойство История данных в значение Использование. После этого необходимо создать регламентное задание, которое будет выполнять обновление истории данных по очереди изменений. Аналогичное свойство присутствует у подчиненных объектов конфигурации: реквизитов (стандартных и обычных), табличных частей и их реквизитов, ресурсов регистров сведений.
По умолчанию свойство История данных установлено в следующие значения:
● справочники, документы, планы видов характеристик, планы счетов, бизнес-процессы, задачи, регистры сведений ‑ значение Не использовать;
● стандартные реквизиты ‑ значение Использовать;
● реквизиты объектов ‑ значение Использовать;
● реквизиты табличных частей ‑ значение Использовать;
● измерения регистров сведений ‑ всегда используется значение Использовать (без возможности отключения);
● ресурсы регистров сведений ‑ значение Использовать.
Таким образом, для типообразующих объектов конфигурации ведение истории надо включать принудительно, а все реквизиты таких объектов автоматически попадают в историю и необходимо принудительно исключать эти реквизиты из истории.
Программная работа с историей данных будет рассмотрена в следующем разделе.
25.3. Использование механизма
25.3.1. Общая информация
Для обращения к истории данных служит свойство глобального контекста ИсторияДанных. Через это свойство доступен менеджер истории данных. Следовательно, обращение к методам программного интерфейса будут выглядеть следующим образом: ИсторияДанных.ИмяВызываемогоМетода(). Для упрощения, в нижеследующем тексте имя свойства глобального контекста будет опускаться.
25.3.2. Управление использованием истории данных
Состояние объекта с точки зрения механизма истории данных можно условно разбить на две части:
1. настройки истории данных, сделанные в конфигураторе;
2. настройки истории данных, сделанные в режиме «1С:Предприятия».
Для доступа к каждой части этих настроек следует использовать различные способы. Для получения настроек, сделанных в конфигураторе, следует использовать доступ к метаданным (объект Метаданные). Для получения нужной информации следует анализировать свойство ИсторияДанных объекта метаданных. Так, для получения признака включения истории данных для справочника Товары, можно использовать следующее выражение:
ОбъектВключен = Метаданные.Справочники.Товары.ИсторияДанных;
Аналогичные конструкции следует использовать для получения информации о реквизитах объектов.
В то же время, для получения информации о том, какие изменения внесены в режиме «1С:Предприятие», необходимо использовать метод ПолучитьНастройки(). При этом в качестве параметра необходимо указать объект конфигурации, настройки которого требуется получить. Метод возвращает объект типа НастройкиИсторииДанных. Если метод вернул значение Неопределено, значит никаких изменений, относительно настроек, сделанных в конфигураторе, с историей данных не выполнялось.
Если известно, что объект конфигурации участвует в версионировании, то можно воспользоваться методом ПолучитьМетаданные() для того, чтобы получить информацию обо всех реквизитах, значения которых сохраняются в истории данных. Результат включает в себя и реквизиты, версионирование которых настроено в конфигураторе, и реквизиты, версионирование которых настроено в режиме «1С:Предприятие».
Установка настроек системы версионирования, выполняется с помощью метода УстановитьНастройки(). Настройки версионирования определяются объектом НастройкиИсторииДанных, которые передаются в качестве параметра метода. В данном объекте необходимо указать как использование истории для объекта в целом (свойство НастройкиИсторииДанных.Использование), так и для реквизитов объекта (свойство НастройкиИсторииДанных.ИспользованиеПолей). Свойство НастройкиИсторииДанных.ИспользованиеПолей является соответствием, в котором ключом является имя реквизита, а значением ‑ признак использования реквизита в истории данных. Если необходимо указать реквизит табличной части, то оно указывается через точку после имени табличной части: ТабличнаяЧасть.Реквизит.
При установке настроек следует учитывать следующую особенность: для реквизитов, перечисленных в объекте НастройкиИсторииДанных, выполняется установка признака участия реквизита в истории данных. Для остальных реквизитов объекта участие в версионировании будет приведено в соответствие с настройками, указанными в конфигураторе.
Также следует помнить, что вызов метода УстановитьНастройки(), где второй параметр установлен в значение Неопределено, приведет к тому, что настройки системы версионирования будут установлены в соответствие с настройками в конфигурации.
25.3.3. Запись версии
25.3.3.1. Обычная обработка
Запись истории изменения данных может быть выполнена двумя способами:
1. Автоматически, самой системой «1С:Предприятие». Основной способ использования механизма истории данных.
2. С помощью встроенного языка, методом ЗаписатьВерсию(). Этот метод используется в том случае, когда необходимо перенести историю данных в системное хранилище из какого-либо другого хранилища. При использовании этого метода запись выполняется сразу в таблицы, которые хранят историю данных, без промежуточной очереди.
В первом случае запись выполняется в три этапа:
1. На первом этапе система выполняет попытку зафиксировать необходимость создания версии данных какого-либо объекта, для которого включено версионирование. Эта попытка выполняется автоматически, при любой записи версионируемого объекта. На этом этапе разработчик может дополнительно выполнить несколько действий:
● Отказаться от формирования версии. Для этого следует установить в значение Истина свойство ЗаписьИсторииДанных.Отказ. Такая потребность может возникнуть в том случае, когда выполняются какие-либо массовые изменения информационной базы, которые не связаны с штатной эксплуатацией системы и которые не должны попадать в историю изменений. Например, это могут быть какие-то действия, связанные с отладкой или разработкой прикладного решения. В этом случае можно анализировать признак нахождения системы в тестовом режиме и устанавливать признак отказа от версионирования.
● Указать дополнительные данные, которые будут записаны вместе с данными версионируемого объекта. Для этого следует использовать метод ЗаписьИсторииДанных.ДобавитьДополнительныеДанные() для добавления собственно дополнительных версиионируемых данных и метод ЗаписьИсторииДанных.ДобавитьПредставлениеПоляДополнительныхДанных() чтобы задать представление дополнительных данных (для отображения в пользовательском интерфейсе).
● Указать необходимость участия записываемой версии в очереди постобработки. Для этого используется свойство версионируемого объекта ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии.
● Указать платформе, что данную версию следует обработать в ускоренном режиме. Для этого используется свойство версионируемого объекта ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи.
2. Чтобы сформировать фактическую версию в истории, разработчику необходимо явно вызвать метод ОбновитьИсторию(). Только после вызова этого метода произойдет фактическое изменение таблиц информационной базы, отвечающих за хранение истории, и сформированную версию можно будет получить для анализа. Обновление истории рекомендуется размещать в регламентном задании, которое будет срабатывать с определенной периодичностью. Частота выполнения регламентного задания определяется количеством версионируемых объектов, интенсивностью их изменения и тем, как часто необходимо получать доступ к историческим данным.
После модификации таблиц, отвечающих за хранение истории, платформа формирует очередь постобработки.
3. Выполняется обработка после записи истории данных. Эта обработка может быть выполнена как собственно методом ОбновитьИсторию(), так и методом ВыполнитьОбработкуПослеЗаписиВерсий() менеджера истории данных.
Многоэтапный способ формирования истории реализован с целью минимизации нагрузки на базу данных во время формирования списка версий при большом объеме изменяемых данных, а также с целью разделить выполнение различных механизмов, связанных с формированием версий данных.
Смотри также:
● Обработка после записи истории данных (см. здесь).
25.3.3.2. Ускоренная обработка
В некоторых случаях может быть необходимо сформировать историю данных (и выполнить связанные действия) сразу, не дожидаясь срабатывания регламентного задания, выполняющего обновление истории данных. Это можно сделать следующими способами:
● Установить флажок Обновлять историю данных сразу после записи. В этом случае ускоренная обработка формирования истории будет происходить для любого объекта этого типа и при любой записи объекта. Такой флажок не рекомендуется устанавливать для видов метаданных, в которых предполагается большое количество объектов и будет выполняться интенсивное изменение этих объектов.
● Использовать свойство ЗаписьИсторииДанных. ОбновлятьИсториюСразуПослеЗаписи при записи объекта. В этом случае ускоренная обработка будет применяться только в исключительных случаях, при выполнении каких-либо условий, которые можно проверить при записи объекта данных.
● Вызов метода встроенного языка ОбновитьИсторию() с передачей в качестве параметра требуемого объекта.
В любом из вышеперечисленных случаев будет выполнен полный цикл обработки истории, но будет обработана не вся очередь версий, а одно изменение одного объекта информационной базы.
25.3.3.3. Добавление данных в версию
Платформа, при записи версии данных, позволяет дополнить собственно данные записываемого объекта некоторыми дополнительными данными. Чтобы это сделать, необходимо для версионируемого объекта использовать метод ЗаписьИсторииДанных.ДобавитьДополнительныеДанные(). В качестве параметра этого метода используется три значения:
● Имя ‑ имя «реквизита» дополнительных данных.
● Значение ‑ значение «реквизита» дополнительных данных. Если в качестве значения параметра Значение передается ТаблицаЗначений, значит, в дополнительные данные добавляется табличная часть. В этом случае структура таблицы значений определяет структуру табличной части.
● Представление ‑ содержит представление «реквизита» дополнительных данных. Представление можно задать или единое для всех языков или задать локализованное представление «реквизита». В первом случае (одно представление для всех языков) представление задается обычной строкой. Во втором случае (локализованное представление) представление задается соответствием, где Ключ определяет код языка, а Значение ‑ собственно представление.
Представление для дополнительных данных можно указать и отдельно от добавления непосредственно данных. Для этого следует использовать метод ЗаписьИсторииДанных.ДобавитьПредставлениеДополнительныхДанных().
25.3.4. Обработка после записи истории
Платформа предоставляет возможность выполнять действия, которые должны происходить только после того, как объект зафиксирован в истории данных. В качестве примера можно рассмотреть ситуацию, когда следует изменить отпускную цену на товар в том случае, если изменилась закупочная цена.
Общая схема работы описана ранее. В данном разделе остановимся на технических особенностях обработки после записи версии данных. Включить версию данных в очередь постобработки можно следующими способами:
● Указав в свойствах объекта конфигурации свойство Выполнять обработку после записи версии истории данных. В этом случае запись объектов данного вида будет приводить к тому, что каждая версия данных будет автоматически попадать в очередь постобработки.
● Использовать свойство ЗаписьИсторииДанных.ВыполнитьОбработкуПослеЗаписиВерсии при записи объекта. В этом случае версия данных попадет в очередь постобработки только при выполнении каких-либо условий, которые можно проверить при записи объекта данных.
Как было сказано выше, формирование собственно истории данных формируется с помощью вызова метода ОбновитьИсторию() менеджера истории данных. Если параметр ВыполнитьОбработкуПослеЗаписиВерсии этого метода установлен в значение Истина, то после того, как платформа завершит формирование истории данных, те версии данных, для которых свойство ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии установлено в значениеИстина, будут помещены в очередь постобработки. Затем платформа инициирует обработку этой очереди вызовом метода ВыполнитьОбработкуПослеЗаписиВерсий().
Во время выполнения данного метода происходит следующее:
● Формируется коллекция версий объектов одного типа (в количестве не более 1 000 версий).
● Вызывается обработчик события ОбработкаПослеЗаписиВерсийИсторииДанных в модуле менеджера соответствующего объекта конфигурации.
В обработчике события ОбработкаПослеЗаписиВерсийИсторииДанных необходимо явным образом удалять каждый обработанный объект из очереди постобработки. Это можно сделать с помощью метода УдалитьИзОбработкиПослеЗаписиВерсий() менеджера истории данных. Если этого не сделать, то версия данных останется в очереди постобработки.
Очистить очередь постобработки можно с помощью метода УдалитьИзОбработкиПослеЗаписиВерсий(). Нужно помнить о том, что удаление из очереди постобработки происходит по времени. В режиме «по умолчанию» из очереди постобработки будут удаляться версии данных, созданные более 20 дней назад (от момента очистки). Параметр Дата метода УдалитьИзОбработкиПослеЗаписиВерсий() позволяет изменить интервал очистки.
Смотри также:
● Устройство механизма истории данных (см. здесь).
25.3.5. Получение списка версий
Во время работы система может формировать достаточно большое количество версий объектов. Однако во время анализа истории все созданные версии могут не потребоваться. В то же время, может возникнуть необходимость отобрать версии объекта, созданные определенным пользователем, в определенный интервал дат и т. д. Выполнить такой отбор можно с помощью метода ВыбратьВерсии().
В качестве параметров метода ВыбратьВерсии() указываются критерии отбора версий, какие данные возвращать, как упорядочивать возвращаемые данные и какое максимальное количество записей можно получить в результате выполнения запроса к истории данных.
Критерий отбора (параметр Отбор функции ВыбратьВерсии()) описывает условия, которым должны соответствовать версии, которые будут возвращены в результате работы функции. Полный перечень элементов отбора приведен в синтакс-помощнике. Версия считается соответствующей отбору, если она соответствует всем критериям отбора, указанным в параметре (условия объединяются «по И»). Если параметр позволяет задать несколько условий (объединив их в массив), то версия будет соответствовать отбору в том случае, если значение в версии соответствует хотя бы одному значению из параметра отбора (несколько условий на одно значение объединяются «по ИЛИ»). Исключением является отбор по параметрам ЗначенияПолей и ИзменениеЗначенийПолей, работа которых будет подробно описана далее. Если в качестве значения отбора передается строка, то, как правило, сравнение с этой строкой выполняется не на полное равенство, а по вхождению ‑ версия считается удовлетворяющей условию, если значение в версии включает в себя значение из отбора.
При заполнении критериев отбора следует помнить о следующих особенностях:
● Для отбора необходимо указать или параметр Метаданные, или параметр Данные. Если указаны оба параметра или оба этих параметра не заполнены ‑ при попытке получить список версий будет сгенерировано исключение. Исключение не будет сгенерировано в том случае, если метаданные параметра Данные совпадают со значением параметра Метаданные.
Если история данных получается по конкретному объекту данных, то перед получением списка версий рекомендуется выполнить обновление истории именно по требуемому объекту с помощью метода ОбновитьИсторию(ЭлементСсылка). Здесь ЭлементСсылка ‑ это тот элемент данных, для которого необходимо получить список версий. В этом случае будет выполнена принудительная актуализация списка версий для одного объекта и метод ВыборВерсий() будет оперировать полным списком версий.
● Для отбора по пользователю используется идентификатор пользователя информационной базы, а не объект ПользовательИнформационнойБазы. Идентификатор пользователя можно получить с помощью метода ПользовательИнформационнойБазы.УникальныйИдентификатор(). Кроме отбора по идентификатору пользователя можно, также, использовать отборы по имени и полному имени пользователя, выполнившего запись версии. Такой отбор, например, может быть использован в том случае, если пользователь, выполнивший запись версии, уже удален, и его уникальный идентификатор получить не представляется возможным.
● Параметр ИзменениеЗначенийПолей позволяет отобрать только те версии, в которых выполнялось изменение значений указанных в параметре полей. Данный параметр отбора не позволяет указать конкретные значения, но позволяет определить сам факт изменения.
● В том случае, если требуется отобрать версии, где на значения полей необходимо наложить конкретные условия, следует использовать параметр ЗначенияПолей. Данный параметр содержит массив структур специального вида. Работа данного параметра более подробно описана далее.
Параметр ЗначенияПолей содержит массив, который содержит элементы типа Структура, следующего состава:
● Ключ Поле ‑ в качестве значения содержит имя проверяемого реквизита объекта. Если необходимо указать реквизит табличной части, то в этом случае имя указывается через точку от имени табличной части, например, Цены.Цена.
● Ключ ЗначениеДоИзменения ‑ в качестве значения содержит значение реквизита, имя которого указано в элементе структуры с ключом Поле, которое было до момента создания версии.
● Ключ ЗначениеПослеИзменения ‑ в качестве значения содержит значение реквизита, имя которого указано в элементе структуры с ключом Поле, которое было в новой версии объекта.
● В структуре всегда должен присутствовать элемент с ключом Поле и хотя бы один элемент, описывающий значение для этого элемента.
● Если в массиве, который передан в качестве параметра ИзменениеЗначенийПолей передано две структуры с одинаковым значением ключа Поле, то такие элементы будут объединяться «по ИЛИ». Однако разноименные условия отбора будут объединяться «по И».
● Отбор позволяет указывать проверки только по условию равенства двух значений. Другие виды условий не поддерживаются.
Рассмотрим пример работы с отбором. В качестве примера будет использован некоторый документ Накладная, который содержит реквизиты Контрагент, Скидка и табличную часть Состав с реквизитами Товар, Цена и Количество.
Если необходимо получить все версии документа, в которых изменяется реквизит Скидка, то отбор будет выглядеть следующим образом:
Отбор = Новый Структура; Отбор.Вставить("Данные", СсылкаНаНакладную); СписокПолей = Новый Массив; СписокПолей.Добавить("Скидка"); Отбор.Вставить("ИзменениеЗначенийПолей", СписокПолей); Версии = ИсторияДанных.ВыбратьВерсии(Отбор);
Если необходимо получить все версии документа, где реквизит Скидка стал, например, 20%, то следует использовать следующий отбор:
Отбор = Новый Структура; Отбор.Вставить("Данные", СсылкаНаНакладную); ИзменениеСкидки = Новый Структура; ИзменениеСкидки.Вставить("Поле", "Скидка") ИзменениеСкидки.Вставить("ЗначениеПослеИзменения", 20); ИзмененияПолей = Новый Массив; ИзмененияПолей.Добавить(ИзменениеСкидки); Отбор.Вставить("ЗначенияПолей", ИзмененияПолей); Версии = ИсторияДанных.ВыбратьВерсии(Отбор);
В выше приведенном примере следует понимать, что в результате такого запроса будет получен список всех версий документа, где значение поля Скидка при записи объекта было равно 20. Получившийся список не есть список версий, в которых значение реквизита было изменено! Для того чтобы получить список версий, где значение скидки стало равно 20 (но все равно, какой эта скидка была до изменения), следует сформировать следующий отбор:
Отбор = Новый Структура; Отбор.Вставить("Данные", СсылкаНаНакладную); ИзменениеСкидки = Новый Структура; ИзменениеСкидки.Вставить("Поле", "Скидка") ИзменениеСкидки.Вставить("ЗначениеПослеИзменения", 20); ИзмененияПолей = Новый Массив; ИзмененияПолей.Добавить(ИзменениеСкидки); Отбор.Вставить("ЗначенияПолей", ИзмененияПолей); СписокПолей = Новый Массив; СписокПолей.Добавить("Скидка"); Отбор.Вставить("ИзменениеЗначенийПолей", СписокПолей); Версии = ИсторияДанных.ВыбратьВерсии(Отбор);
В том случае, если необходимо получить только те версии, в которых реквизит Скидка был изменен с 10% на 20%, следует использовать следующий отбор:
Отбор = Новый Структура; Отбор.Вставить("Данные", СсылкаНаНакладную); ИзменениеСкидки = Новый Структура; ИзменениеСкидки.Вставить("Поле", "Скидка") ИзменениеСкидки.Вставить("ЗначениеДоИзменения", 10); ИзменениеСкидки.Вставить("ЗначениеПослеИзменения", 20); ИзмененияПолей = Новый Массив; ИзмененияПолей.Добавить(ИзменениеСкидки); Отбор.Вставить("ЗначенияПолей", ИзмененияПолей); Версии = ИсторияДанных.ВыбратьВерсии(Отбор);
Также система позволяет выполнять более сложные запросы к истории. Например, необходимо получить те версии документа Накладная, где цена определенного товара стала равна 15 или 35 денежным единицам:
Отбор = Новый Структура; Отбор.Вставить("Данные", СсылкаНаНакладную); Условие1 = Новый Структура; Условие1.Вставить("Поле", "Состав.Цена") Условие1.Вставить("ЗначениеПослеИзменения", 15); Условие2 = Новый Структура; Условие2.Вставить("Поле", "Состав.Цена") Условие2.Вставить("ЗначениеПослеИзменения", 35); Условие3 = Новый Структура; Условие3.Вставить("Поле", "Состав.Товар") Условие3.Вставить("ЗначениеПослеИзменения", СсылкаНаТовар); ИзмененияПолей = Новый Массив; ИзмененияПолей.Добавить(Условие1); ИзмененияПолей.Добавить(Условие2); ИзмененияПолей.Добавить(Условие3); Отбор.Вставить("ЗначенияПолей", ИзмененияПолей); СписокПолей = Новый Массив; СписокПолей.Добавить("Состав.Цена"); Отбор.Вставить("ИзменениеЗначенийПолей", СписокПолей); Версии = ИсторияДанных.ВыбратьВерсии(Отбор);
В примере, размещенном выше, сформировано следующее условие:
1. Рассматривается история документа, который определен значением переменной СсылкаНаНакладную.
2. Из истории выбираются только те версии, в которых в табличной части есть строка, удовлетворяющая любому из условий:
● реквизит Товар заполнен значением из переменной СсылкаНаТовар;
● значение реквизита Цена стало равно 15 или 35;
● реквизит Цена был изменен перед записью версии.
25.3.6. Получение данных версии
Основное предназначение истории данных ‑ это возможность получить данные конкретной версии. Система версионирования платформы «1С:Предприятие» предоставляет несколько различных способов получения этой информации.
Метод СформироватьПоВерсии() позволяет сразу получить объект данных требуемой версии по его ссылке или ключу записи. Для получения данных необходимо иметь ссылку (или ключ записи) на объект, версию которого надо получить, а также собственно номер версии. В результате в памяти будет сформирован объект соответствующего типа, который можно будет заново записать в базу данных.
Пример:
// Восстановление версии СсылкаНаОбъект = ПолучитьСсылкуНаОбъект(); НомерВерсии = ОпределитьТребуемыйНомерВерсии(); Объект = ИсторияДанных.СформироватьПоВерсии(СсылкаНаОбъект, НомерВерсии); Объект.Записать();
В том случае, если выполняется попытка получить данные версии, которая соответствует удалению объекта ‑ будет получен объект, заполненный данными, которые были в объекте в момент удаления.
Разработчик может вмешаться в процесс формирования объекта на основании версии данных. Для этого можно использовать обработчик события ОбработкаФормированияПоВерсииИсторииДанных() модуля объекта и набора записей регистра сведений. Данный обработчик вызывается после вызова метода СформироватьПоВерсии() и во время интерактивного перехода на версию.
При создании формы разработчик также имеет возможность понять, что форма открывается не с актуальным объектом базы данных, а с версией объекта из истории данных. Для этого предназначен параметр формы объекта (или формы набора записей регистра сведений) НомерВерсииПереходаНаВерсиюИсторииДанных. Если значение этого параметра отлично от Неопределено, значит, открывается форма, в которой данные соответствуют версии с номером, указанной в данном параметре.
Метод СформироватьПоВерсии() для подчиненного регистра сведений формирует набор записей на основании ключа записи. Сформированный набор записей содержит все строки набора записи, которые могут быть получены из базы данных по указанному ключу. Следовательно, при записи такого набора записей будут обновлены все записи набора, а не только одна запись, которая восстанавливается из истории. Это означает, что в том случае, когда между получением данных версии и записью этих данных произойдет изменение прочитанных данных из другого сеанса, то данные, внесенные из другого сеанса, будут утеряны. Для того чтобы предотвратить такую потерю, необходимо накладывать блокировки на изменяемые данные.
Если нет необходимости получать данные версии непосредственно в объект требуемого типа, можно использовать методПолучитьДанныеВерсии(), который возвращает данные необходимой версии в качестве структуры, где ключом выступает имя реквизита, а значением ‑ значение реквизита.
Пример:
// Получение данных версии СсылкаНаОбъект = ПолучитьСсылкуНаОбъект(); НомерВерсии = ОпределитьТребуемыйНомерВерсии(); ДанныеВерсии = ИсторияДанных.ПолучитьДанныеВерсии(СсылкаНаОбъект, НомерВерсии); Сообщить(ДанныеВерсии.ВерсионируемыйРеквизит);
Кроме получения данных конкретной версии, история данных позволяет получить список версионируемых реквизитов (и их представлений) для каждой версии объекта. Данная возможность позволяет определить различия текущего набора версионируемых реквизитов и набора версионируемых реквизитов необходимой версии. Состав версионируемых реквизитов позволяет получить метод ПолучитьМетаданные(). Следует понимать, что будет получен не объект метаданных, а структура со списком реквизитов. С помощью этого метода не получится сравнить свойства версионируемых реквизитов. Однако результат работы данного метода позволит понять, какие различия в структуре версионируемых реквизитов существуют между метаданными и настройками версионирования, которые были в момент создания версии с определенным номером и текущей структурой метаданных и настройках версионирования.
25.3.7. Комментарий к версии
Любая версия данных может обладать комментарием, который позволяет каким-либо образом прокомментировать версию. Для изменения комментария следует использовать метод ЗаписатьКомментарий(). Максимальный размер комментария составляет 1024 символа.
Также система предоставляет возможность указывать комментарий непосредственно во время записи объекта данных. Для этого необходимо использовать свойство ЗаписьИсторииДанных.КомментарийВерсии при записи версионируемого объекта. Комментарий будет формироваться для той версии, которая будет создана при текущей записи объекта.
25.3.8. Удаление версии
В случае необходимости, может потребоваться удаление одной, нескольких или всех версий в информационной базе. Для этого существует метод УдалитьВерсии() и метод глобального контекста УдалитьДанныеИнформационнойБазы().
С помощью метода УдалитьВерсии() предоставляется возможность выполнить следующие действия:
1. Удалить все исторические данные для какого-либо объекта конфигурации. Для этого следует вызвать метод с указанием в качестве параметра объекта конфигурации: УдалитьВерсии(Метаданные.Справочники.Товары).
2. Удалить данные для какого-либо объекта конфигурации ранее указанной даты: УдалитьВерсии(Метаданные.Справочники.Товары, НачалоГода(ТекущаяДата())).
3. Удалить набор версий какого-либо объекта данных: УдалитьВерсии(СсылкаНаОбъект, НачалоИнтервала, ОкончаниеИнтервала).
4. Удалить все версии какого-либо объекта данных ранее указанной даты: УдалитьВерсии(СсылкаНаОбъект, НачалоГода(ТекущаяДата())).
Особенности удаления истории данных с включенным разделением см. здесь.
25.3.9. Определение различий между версиями
Типовой задачей при работе с историей данных является получение разницы между версиями. Для этого существует метод ПолучитьРазличияВерсий(). Следует понимать, что термин «различие» не предполагает какого-либо вычисления, в том числе и какого-либо анализа изменений по промежуточным версиям (если таковые есть). Различие между версиями предполагает, что система вернет значения версионируемых реквизитов для двух указанных версий, а интерпретацией полученного результата будет заниматься разработчик. При сравнении версий не производится попытки приведения типов. Следовательно, если у значения реквизита изменился тип, значит, значения реквизита до и после изменения будут различаться. Для получения различия между версиями необходимо указать требуемый объект и две версии: версия после изменения (версия, лежащая правее на шкале времени) и версия до изменения (версия, лежащая левее на шкале времени). Если номер версии до изменения не указан, то сравнение будет выполняться между указанной версией и ее предыдущей.
При формировании результата система опирается на структуру метаданных, которая была в момент формирования версии после изменения (второй параметр метода). Из этого следует следующая особенность: если в версии до изменения был какой-либо версионируемый реквизит, а в версии после изменения такого реквизита нет, то разница между версиями не будет содержать никакой информации по удаленному реквизиту. При рассмотрении обратной ситуации (в версии до нет реквизита, а в версии после реквизит появился) в качестве значения до изменения будет выступать значение по умолчанию для соответствующего типа.
В качестве результата исполнения метода выступает структура, где ключом выступает имя версионируемого реквизита или табличной части, а в качестве значения выступает структура (для реквизита) или массив структур (для табличной части) с информацией о значениях до и после изменения.
Значение, описывающее реквизит, представляет собой структуру со следующим составом элементов:
● Ключ ЗначениеПослеИзменения, значение ‑ значение реквизита, которое было в более старшей версии.
● Ключ ЗначениеДоИзменения, значение ‑ значение реквизита, которое было в более ранней версии. Если в более ранней версии реквизит отсутствовал или для него было отключено версионирование, то элемент структуры с ключом ЗначениеДоИзменения будет отсутствовать.
Если версионируемый реквизит имеет ссылочный тип, то информация о ссылке передается структурой, которая содержит два элемента: с ключом Представление ‑ представление, которое было у ссылочного объекта в момент формирования версии и Ссылка ‑ собственно значение ссылки.
Значение, описывающее табличную часть, представляет собой массив, где каждый элемент описывает изменения в строке табличной части. Каждая строка является структурой со следующими элементами:
● Ключ НомерСтрокиВВерсииПослеИзменения. Значение описывает номер строки в табличной части для более поздней версии.
● Ключ НомерСтрокиВВерсииДоИзменения. Значение описывает номер строки в табличной части для более ранней версии.
● Ключ ВидИзмененияСтроки. Значение описывает вид изменения строки табличной части. В зависимости от этого значения изменяется состав структуры, расположенной в элементе Поля.
● Ключ Поля. В качестве значения будет выступать структура, описывающая выполненные изменения. Содержимое структуры зависит от вида изменения строки:
● Создание строки. Для каждого реквизита табличной части, участвующего в версионировании, будет создан свой элемент. Ключом элемента будет имя реквизита, а значением ‑ значение, установленное в реквизит.
● Изменение строки. Для каждого реквизита табличной части, участвующего в версионировании, будет создан свой элемент. Ключом элемента будет имя реквизита, а значением ‑ структура, описанная выше (изменение реквизита объекта).
● Удаление строки. Для каждого реквизита табличной части, участвующего в версионировании, будет создан свой элемент. Ключом элемента будет имя реквизита, а значением ‑ значение, которое было в реквизите до операции удаления.
● Перемещение строки. Данная операция никак не отражается в списке полей.
25.3.10. История данных и расширение
Историю данных можно использовать для собственных и заимствованных объектов расширения конфигурации, при этом:
● Для собственного объекта расширения:
● История ведется как для собственного объекта конфигурации;
● Данные истории будут удалены после того, как расширение полностью удалено из информационной базы.
● Для заимствованного объекта расширения:
● Использование в истории данных можно настроить только для реквизитов, добавленных в расширении. Собственно ведение истории определяется настройками заимствованного объекта.
● Удаление расширения не приводит к очистке истории (аналогично поведению истории данных без расширения в случае удаления версионируемого реквизита объекта).