Отчет разрабатывался для УПП 1.3 на платформе 8.3.16.1148, но думаю, можно использовать и для других конфигураций таких как УТ 10.3 и т.д.
Подключение к приемнику происходит через COM-соединение.
Отчет на СКД генерится программно. Помогает детально посмотреть разницу по выбранному регистру накопления. Есть отборы по периоду, организации и регистру. Сопоставление элементов справочников и документов идет по ГУИДу. Данные о подключениях и фильтрах можно сохранить.
Разрабатывался как универсальный для любого регистра накопления.
Данные выводятся в таблицу, сгруппированные в колонках по текущей и сравниваемой базе в разрезе измерений регистра, исключил поля: активность, номер строки, период, момент времени, регистратор, вид движения. Сразу оговорюсь, на больших данных отчет формируется не быстро.
Основа в модуле объекта, можно рассмотреть как пример:
Перем ИБПриемник;
Перем МассивПолей;
// Формирование и вывод отчета СКД программно
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
УстановитьПривилегированныйРежим(Истина);
Если Не ПроверитьЗаполнение() Тогда
Возврат;
КонецЕсли;
Если ИБПриемник = Неопределено Тогда
ИБПриемник = СоздатьПодключитьИВернутьИБПриемник();
КонецЕсли;
Если ТипЗнч(ИБПриемник) = Тип("Булево") и Не ИБПриемник Тогда
Возврат;
КонецЕсли;
СтандартнаяОбработка = Ложь;
ДанныеТекущейБазы = ПолучитьДанныеТекущейБазы();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
Выборка = ПолучитьДанныеДругойБазы();
Пока Выборка.Следующий() Цикл
НовСтр = ДанныеТекущейБазы.Добавить();
ЗаполнитьЗначенияСвойств(НовСтр, Выборка);
Для каждого Стр Из МассивПолей Цикл
ИмяПоля = Стр.Ключ;
Если Выборка[ИмяПоля] = Неопределено Тогда
Продолжить;
КонецЕсли;
ТипТам = ИБПРиемник.XMLТипЗнч(Выборка[ИмяПоля]).typename;
// Определяем документ
Если Найти(ТипТам, "DocumentRef")>0 Тогда
ГУИДТам = ИБПРиемник.String(Выборка[ИмяПоля].uuid());
Имя = СтрЗаменить(ТипТам, "DocumentRef.", "");
ТипТут = Документы[Имя];
Значение = ТипТут.ПолучитьСсылку(Новый УникальныйИдентификатор(ГУИДТам));
// Определяем справочник
ИначеЕсли Найти(ТипТам, "CatalogRef")>0 Тогда
ГУИДТам = ИБПРиемник.String(Выборка[ИмяПоля].uuid());
Имя = СтрЗаменить(ТипТам, "CatalogRef.", "");
ТипТут = Справочники[Имя];
Значение = ТипТут.ПолучитьСсылку(Новый УникальныйИдентификатор(ГУИДТам));
// Определяем перечисление
ИначеЕсли Найти(ТипТам, "EnumRef")>0 Тогда
ПеречислениеТам = ИБПРиемник.String(Выборка[ИмяПоля]);
Имя = СтрЗаменить(ТипТам, "EnumRef.", "");
Попытка
СписокЭлементовПеречисления = ПолучитьСписокЭлементовПеречисления(Имя);
Значение = ?(ЗначениеЗаполнено(ПеречислениеТам), СписокЭлементовПеречисления.Получить(ПеречислениеТам), Перечисления[Имя].ПустаяСсылка());
Исключение
ТекстОшибки = ОписаниеОшибки();
#Если Клиент Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстОшибки;
Сообщение.Сообщить();
#КонецЕсли
ЗаписьЖурналаРегистрации("Ошибка получения перечисления "+ПеречислениеТам, УровеньЖурналаРегистрации.Ошибка,,,ТекстОшибки);
КонецПопытки;
Иначе
Значение = Выборка[ИмяПоля];
КонецЕсли;
НовСтр[ИмяПоля] = Значение;
КонецЦикла;
КонецЦикла;
ПоляГруппировок = Новый Массив;
ПоляКолонок = Новый Массив;
ПоляКолонок.Добавить("Статус");
НовыйСКД = Новый СхемаКомпоновкиДанных;
Если НовыйСКД.ИсточникиДанных.Количество() Тогда
ИсточникДанных = НовыйСКД.ИсточникиДанных[0];
Иначе
ИсточникДанных = НовыйСКД.ИсточникиДанных.Добавить();
ИсточникДанных.Имя = "ИсточникДанных1";
ИсточникДанных.ТипИсточникаДанных = "local";
КонецЕсли;
Если НовыйСКД.НаборыДанных.Количество() Тогда
НаборДанных = НовыйСКД.НаборыДанных[0];
Иначе
НаборДанных = НовыйСКД.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
НаборДанных.Имя = "НаборДанных1";
НаборДанных.ИсточникДанных = ИсточникДанных.Имя;
НаборДанных.ИмяОбъекта = "ТаблицаДанных";
КонецЕсли;
Для каждого Стр Из МассивПолей Цикл
ИмяПоля = Стр.Ключ;
Если Стр.Значение.Типы()[0] = Тип("Число") Тогда
Ресурс = НовыйСКД.ПоляИтога.Добавить();
Ресурс.ПутьКДанным = ИмяПоля;
Ресурс.Выражение = "Сумма("+ИмяПоля+")";
ИначеЕсли ПоляКолонок.Найти(ИмяПоля) = Неопределено Тогда
ПоляГруппировок.Добавить(ИмяПоля);
КонецЕсли;
ПолеСКД = НаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеСКД.Поле = ИмяПоля;
ПолеСКД.ПутьКДанным = ИмяПоля;
ПолеСКД.Заголовок = ИмяПоля;
ПолеСКД.ТипЗначения = Стр.Значение;
КонецЦикла;
НастройкиСКД = НовыйСКД.ВариантыНастроек[0].Настройки;
ОбщиеНастройки = НастройкиСКД.ПараметрыВывода.Элементы;
ГоризонтальноеРасположениеОбщихИтогов = ОбщиеНастройки.Найти("ГоризонтальноеРасположениеОбщихИтогов");
ГоризонтальноеРасположениеОбщихИтогов.Значение = РасположениеИтоговКомпоновкиДанных.Нет;
ГоризонтальноеРасположениеОбщихИтогов.Использование = Истина;
Попытка
// Формируем настройку как Таблицу СКД
Таблица =НастройкиСКД.Структура.Добавить(Тип("ТаблицаКомпоновкиДанных"));
Таблица.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
СтрокаТаблицы = Таблица.Строки.Добавить();
СтрокаТаблицы.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
СтрокаТаблицы.Порядок.Элементы.Добавить(Тип("АвтоЭлементПорядкаКомпоновкиДанных"));
Для каждого ТекПоле Из ПоляГруппировок Цикл
ПолеГруппировки = Новый ПолеКомпоновкиДанных(ТекПоле);
ГруппировкаСтроки = СтрокаТаблицы.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ГруппировкаСтроки.Поле = ПолеГруппировки;
ГруппировкаСтроки.Использование = Истина;
ГруппировкаСтроки.ТипГруппировки = ТипГруппировкиКомпоновкиДанных.Элементы;
КонецЦикла;
АвтоВыбранноеПолеКомпоновкиДанных = СтрокаТаблицы.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
АвтоВыбранноеПолеКомпоновкиДанных.Использование = Истина;
КолонкаТаблицы = Таблица.Колонки.Добавить();
КолонкаТаблицы.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
КолонкаТаблицы.Порядок.Элементы.Добавить(Тип("АвтоЭлементПорядкаКомпоновкиДанных"));
ПолеГруппировки = Новый ПолеКомпоновкиДанных("Статус");
ГруппировкаКолонки = КолонкаТаблицы.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ГруппировкаКолонки.Поле = ПолеГруппировки;
ГруппировкаКолонки.Использование = Истина;
ГруппировкаКолонки.ТипГруппировки = ТипГруппировкиКомпоновкиДанных.Элементы;
Для каждого Стр Из МассивПолей Цикл
ВыбранноеПолеКомпоновкиДанных = НастройкиСКД.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПолеКомпоновкиДанных.Использование = Истина;
ВыбранноеПолеКомпоновкиДанных.Поле = Новый ПолеКомпоновкиДанных(Стр.Ключ);
КонецЦикла;
ЭлементПорядкаКомпоновкиДанных = НастройкиСКД.Порядок.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
ЭлементПорядкаКомпоновкиДанных.Использование = Истина;
ЭлементПорядкаКомпоновкиДанных.Поле = Новый ПолеКомпоновкиДанных("Статус");
ЭлементПорядкаКомпоновкиДанных.ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных.Убыв;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(НовыйСКД, НастройкиСКД, ДанныеРасшифровки);
ВнешниеНаборы = Новый Структура("ТаблицаДанных", ДанныеТекущейБазы);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ОписаниеОшибки();
Сообщение.Сообщить();
КонецПопытки;
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Функция ПолучитьСписокЭлементовПеречисления(ИмяПеречисления) Экспорт
СписокЭлементовПеречисления = Новый Соответствие;
Попытка
КоллекцияЭлементовПеречисления = Перечисления[ИмяПеречисления];
Исключение
Возврат СписокЭлементовПеречисления;
КонецПопытки;
Для каждого ЭлементПеречисления Из КоллекцияЭлементовПеречисления Цикл
СписокЭлементовПеречисления.Вставить(Строка(ЭлементПеречисления), ЭлементПеречисления);
КонецЦикла;
Возврат СписокЭлементовПеречисления;
КонецФункции // ПолучитьСписокЭлементовПеречисления()
// Организация - реквизит отчета, заполняется на форме.
Функция ПолучитьДанныеТекущейБазы()
ТекстЗапроса = ПолучитьТекстЗапроса("Эта база");
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("Организация", Организация);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Возврат РезультатЗапроса;
КонецФункции
// Организация - реквизит отчета, заполняется на форме.
Функция ПолучитьДанныеДругойБазы()
Если ЗначениеЗаполнено(Организация) Тогда
ГуидОрганизации = ИБПриемник.NewObject("УникальныйИдентификатор", XMLСтрока(Организация.УникальныйИдентификатор()));
ОрганизацияВПриемнике = ИБПриемник.Справочники.Организации.ПолучитьСсылку(ГуидОрганизации);
Если Не ИБПриемник.ОбщегоНазначения.СсылкаСуществует(ОрганизацияВПриемнике) Тогда
ОрганизацияВПриемнике = ИБПриемник.Справочники.Организации.НайтиПоНаименованию(Организация.Наименование);
КонецЕсли;
Если Не ИБПриемник.ОбщегоНазначения.СсылкаСуществует(ОрганизацияВПриемнике) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Заданная организация в другой базе не найдена, данные не получены";
Сообщение.Сообщить();
Возврат Новый ТаблицаЗначений;
КонецЕсли;
КонецЕсли;
ТекстЗапроса = ПолучитьТекстЗапроса("Сравниваемая база");
Запрос = ИБПриемник.NewObject("Запрос");
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("Организация", ОрганизацияВПриемнике);
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Возврат РезультатЗапроса;
КонецФункции
// Формирование текста запроса, ИмяРегистра - реквизит отчета, заполняется на форме.
Функция ПолучитьТекстЗапроса(СтатусБазы)
МассивПолей = Новый Соответствие;
ПолноеИмя = "РегистрНакопления."+ИмяРегистра;
Мета = Метаданные.НайтиПоПолномуИмени(ПолноеИмя);
Если МассивПолей.Получить("Статус") = Неопределено Тогда
МассивПолей.Вставить("Статус", Новый ОписаниеТипов("Строка",,,,Новый КвалификаторыСтроки(100)));
КонецЕсли;
СписокПолей = "
| Выразить("""+СтатусБазы+""" как Строка(100)) КАК Статус,";
ПоляИсключения = Новый Массив;
ПоляИсключения.Добавить("Активность");
ПоляИсключения.Добавить("НомерСтроки");
ПоляИсключения.Добавить("Период");
ПоляИсключения.Добавить("МоментВремени");
ПоляИсключения.Добавить("Регистратор");
ПоляИсключения.Добавить("ВидДвижения");
ПолучитьПоля(Мета.СтандартныеРеквизиты , СписокПолей, ПоляИсключения);
ПолучитьПоля(Мета.Измерения, СписокПолей);
ПолучитьПоля(Мета.Ресурсы, СписокПолей);
ПолучитьПоля(Мета.Реквизиты, СписокПолей);
СписокПолей = Лев(СписокПолей, СтрДлина(СписокПолей)-1);
Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ "+СписокПолей + "
| ИЗ " +"
| " + ПолноеИмя;
Если ЗначениеЗаполнено(Организация) и Найти(СписокПолей, "Организация") > 0 Тогда
Текст = Текст+"
| ГДЕ Организация = &Организация";
КонецЕсли;
Возврат Текст;
КонецФункции
Процедура ПолучитьПоля(Коллекция, СписокПолей, ПоляИсключения = Неопределено)
Перем Поле;
ПоляИсключения = ?(ПоляИсключения = Неопределено, Новый Массив, ПоляИсключения);
Для каждого Поле Из Коллекция Цикл
Если ПоляИсключения.Найти(Поле.Имя)<>Неопределено Тогда
Продолжить;
КонецЕсли;
СписокПолей = СписокПолей+"
| " + Поле.Имя+",";
Если МассивПолей.Получить(Поле.Имя) = Неопределено Тогда
МассивПолей.Вставить(Поле.Имя, Поле.Тип);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Создаем подключение к приемнику по COM соединению
// Сервер, ИмяБазы, Пользователь, Пароль - реквизиты отчета, заполняются на форме.
Функция СоздатьПодключитьИВернутьИБПриемник() Экспорт
врСтрокаПодключения = "Srvr="""+СокрЛП(Сервер)+""";Ref="""+ИмяБазы+""";Usr="""+СокрЛП(Пользователь)+""";Pwd="""+СокрЛП(Пароль)+""";";
Текст = "Выполнено подключение к базе: "+"Srvr="""+СокрЛП(Сервер)+""";Ref="""+ИмяБазы+""";Usr="""+СокрЛП(Пользователь)+"""";
Попытка
ИБПриемник = Новый COMObject("v83.COMConnector");
Исключение
ЗаписьЖурналаРегистрации("ПодключениеКПериферийнойБазе", УровеньЖурналаРегистрации.Информация, , , "Не удалось создать v83.COMConnector");
КонецПопытки;
Открыта = Ложь;
ЗаписьЖурналаРегистрации("ПодключениеКПериферийнойБазе", УровеньЖурналаРегистрации.Информация, , , "Создан v83.COMConnector");
Попытка
ИБПриемник = ИБПриемник.connect(врСтрокаПодключения);
#Если Клиент Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Текст;
Сообщение.Сообщить();
#КонецЕсли
ЗаписьЖурналаРегистрации("ПодключениеКПериферийнойБазе", УровеньЖурналаРегистрации.Информация, , , врСтрокаПодключения);
Возврат ИБПриемник;
Исключение
ОписаниеОшибки = ИнформацияОбОшибке().Описание;
#Если Клиент Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ОписаниеОшибки;
Сообщение.Сообщить();
#КонецЕсли
ЗаписьЖурналаРегистрации("ПодключениеКПериферийнойБазе", УровеньЖурналаРегистрации.Информация, , , ОписаниеОшибки);
Возврат Открыта;
КонецПопытки;
Возврат Открыта;
КонецФункции
МассивПолей = Новый Соответствие;
Модуль формы отчета:
Процедура ПроверитьПодключениеНажатие(Элемент)
Если Не ПроверитьЗаполнение() Тогда
Возврат;
КонецЕсли;
УстановитьПривилегированныйРежим(Истина);
СоздатьПодключитьИВернутьИБПриемник();
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Процедура ИмяРегистраНачалоВыбораИзСписка(Элемент, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
СписокРегистров = Новый СписокЗначений;
Для каждого Стр Из Метаданные.РегистрыНакопления Цикл
СписокРегистров.Добавить(Стр.Имя);
КонецЦикла;
Значение = ВыбратьИзСписка(СписокРегистров, Элемент, СписокРегистров.НайтиПоЗначению(ИмяРегистра));
Если Значение <> Неопределено Тогда
ИмяРегистра = Значение;
КонецЕсли;
КонецПроцедуры
Для программного вывода отчета помогла статья //infostart.ru/public/1179039/, единственный момент, автор не рассмотрел вариант настройки СКД как таблица, в моем случае она и нужна, возможно, кому-то будет полезной как дополнение.
Так же предлагаю решения вашему вниманию:
Список документов и их количество в базе.
Универсальная выгрузка и загрузка зарегистрированных справочников и документов по узлу.