История такая. Есть две базы - одна рабочая, другая тестовая копия. Так получилось (не будем обсуждать почему), что некий пользователь проделал большой объём работы в тестовой базе - он изменил в старых периодах около тысячи существующих документов и создал несколько новых. Работал он день и ночь на протяжении нескольких дней, не спал и не ел (по крайней мере примерно так мне описала ситуацию главный бухгалтер). В общем, в итоге возникла необходимость перенести эти изменённые документы из тестовой базы обратно в рабочую. Переносить ручками было нереально (большой объём, точных сведений что и где менялось нет), поэтому нужно было как-то заавтоматизировать процесс переноса.
Чтобы выполнить эту задачу, нужно было ответить на два вопроса - что переносить и чем переносить? Над вторым вопросом долго думать не пришлось, так как есть палочка-выручалочка под названием ВыгрузкаЗагрузкаДанныхXML82.epf. Теперь нужно было ответить на первый вопрос - что? Предоставить список изменённых документов пользователь, разумеется, не смог. Поэтому единственный способ узнать что же нужно переносить - это проанализировать журнал регистрации (нужно выгрузить все объекты, которые этот пользователь изменил/создал в указанный период времени).
По итогам в обработку ВыгрузкаЗагрузкаДанныхXML82.epf версии 2.1.8 была добавлена ещё одна кнопка с незамысловатым названием Кнопка1 и нарисован такой обработчик её нажатия:
Процедура Кнопка1Нажатие(Элемент)
Дата1 = Дата(2015, 08, 01); // Отбор записей ЖР по периоду: дата начала периода (или Неопределено)
Дата2 = Дата(2015, 08, 31); // Отбор записей ЖР по периоду: дата окончания периода (или Неопределено)
Пользователь = "Иванов И.И."; // Отбор записей ЖР по пользователю: имя пользователя (или Неопределено)
// {{ Получаем таблицу значений с записями ЖР
ТЗ = Новый ТаблицаЗначений;
Фильтр = Новый Структура;
Если ЗначениеЗаполнено(Дата1) Тогда
Фильтр.Вставить("ДатаНачала", НачалоДня(Дата1));
КонецЕсли;
Если ЗначениеЗаполнено(Дата2) Тогда
Фильтр.Вставить("ДатаОкончания", КонецДня(Дата2));
КонецЕсли;
Если ЗначениеЗаполнено(Пользователь) Тогда
Фильтр.Вставить("Пользователь", Пользователь);
КонецЕсли;
МассивСобытий = Новый Массив;
МассивСобытий.Добавить("_$Data$_.New");
МассивСобытий.Добавить("_$Data$_.Post");
МассивСобытий.Добавить("_$Data$_.Unpost");
МассивСобытий.Добавить("_$Data$_.Update");
Фильтр.Вставить("Событие", МассивСобытий);
МассивМетаданных = Новый Массив;
Для Каждого МетаДок Из Метаданные.Документы Цикл
МассивМетаданных.Добавить(МетаДок);
КонецЦикла;
Фильтр.Вставить("Метаданные", МассивМетаданных);
ВыгрузитьЖурналРегистрации(ТЗ, Фильтр, "Дата, Пользователь, Событие, Метаданные, Данные, ПредставлениеДанных");
// Получаем таблицу значений с записями ЖР }}
// Для целей отладки можно посмотреть содержимое таблицы с записями ЖР:
//Если ТЗ.ВыбратьСтроку() = Неопределено Тогда
// Возврат;
//КонецЕсли;
ТЗ.Свернуть("Метаданные, Данные"); // удаляем дубли
// {{ Формируем соответствие, у которого в качестве ключей - тип метаданных,
// а в качестве значений - массивы ссылок на объекты
// (возможно, лучше было бы использовать ДеревоЗначений, но уже лень переделывать)
ВремСоотв = Новый Соответствие;
Для Каждого СтрокаТЗ Из ТЗ Цикл
МассивДанных = ВремСоотв[СтрокаТЗ.Метаданные];
Если МассивДанных = Неопределено Тогда
МассивДанных = Новый Массив;
КонецЕсли;
МассивДанных.Добавить(СтрокаТЗ.Данные);
ВремСоотв[СтрокаТЗ.Метаданные] = МассивДанных;
КонецЦикла;
// Формируем соответствие, у которого в качестве ключей - тип метаданных,
// а в качестве значений - массивы ссылок на объекты }}
КолвоОшибок = 0;
// {{ Настраиваем какие типы объектов выгружать и какие при этом отборы использовать
Для Каждого КлючИЗначение Из ВремСоотв Цикл
ТекИмяМетаданных = КлючИЗначение.Ключ;
ТекМассивСсылок = КлючИЗначение.Значение;
ТекОбъектМД = Метаданные.НайтиПоПолномуИмени(ТекИмяМетаданных);
ТекущаяСтрока = ДеревоМетаданных_КопияФормы.Строки.Найти(ТекОбъектМД, "Метаданные", Истина);
Если ТекущаяСтрока = Неопределено Тогда
Сообщить("Не найдена строка дерева метаданных для " + ТекИмяМетаданных);
КолвоОшибок = КолвоОшибок + 1;
Продолжить;
КонецЕсли;
ЭлементыФормы.ДеревоМетаданных.ТекущаяСтрока = ТекущаяСтрока;
ДеревоМетаданныхПриАктивизацииСтроки(Неопределено); // выполним стандартную настройку построителя
// Очищаем существующие отборы построителя
КолвоОтборов = Построитель.Отбор.Количество();
Для ОбратныйИндекс = 1 По КолвоОтборов Цикл
ТекИндекс = КолвоОтборов - ОбратныйИндекс;
Построитель.Отбор.Удалить(ТекИндекс);
КонецЦикла;
// Добавляем отбор по списку ссылок
ЭлемОтбора = Построитель.Отбор.Добавить("Ссылка");
ЭлемОтбора.ВидСравнения = ВидСравнения.ВСписке;
ЭлемОтбора.Значение = Новый СписокЗначений;
ЭлемОтбора.Значение.ЗагрузитьЗначения(ТекМассивСсылок);
ЭлемОтбора.Использование = Истина;
ТекущаяСтрока.НастройкиПостроителя = Построитель.ПолучитьНастройки();
ТекущаяСтрока.ИспользоватьОтбор = ИСТИНА;
ТекущаяСтрока.Выгружать = Истина; // устанавливаем флажок "Выгружать" для текущего типа объектов
// Для целей отладки можно вывести какие объекты попали в отбор:
//Сообщить("Для объектов с типом " + ТекИмяМетаданных + " добавлен отбор по ссылке со следующим списком значений:");
//Для Каждого ЭлемСЗ Из СЗ Цикл
// Сообщить(" - " + ЭлемСЗ.Значение);
//КонецЦикла;
КонецЦикла;
// Настраиваем какие типы объектов выгружать и какие при этом отборы использовать }}
Сообщить("Настройка отборов завершена! Ошибок: " + КолвоОшибок);
КонецПроцедуры
На выходе получили требуемый XML, который и загрузили в рабочую базу.
P.S. Кстати говоря, в модификации самой обработки ВыгрузкаЗагрузкаДанныхXML82.epf особой нужды, скорее всего, нет. Полагаю, что можно нарисовать отдельную обработку-обёртку, которая получит из ЖР необходимые данные, создаст объект штатной обработки ВыгрузкаЗагрузкаДанныхXML82.epf и программно настроит у неё список выгружаемых объектов. Но это уже совсем другая история...