Привет, коллеги! В своей работе я часто встречаюсь с различными реализациями выгрузок дополнительной информации в Битрикс через всякие кастомные модули для Битрикс, сложные обмены через файлы или фтп папки.
Хочу поделиться с вами простым и универсальным решением - очень простой доработкой стандартного модуля обмена Битрикс, которая позволит выгружать и главное обновлять ЛЮБЫЕ данные в Highload-блоки Битрикс.
Возможные кейсы
- выгрузка дополнительных сведений для карт лояльности
- выгрузка видов цен контрагентов из договоров
- выгрузка исключений цен и расширение механизмов ценообразования
- выгрузка любых регистров сведений или справочников или других результатов запросов
- выгрузка резервов, остатков, взаиморасчетов и любых других регистров накоплений
- выгрузка детализаций документооборота для б2б системы
- выгрузка дополнительный сведений или контента для товаров
- выгрузка дополнительных механизмов контроля доступа
- выгрузка вообще любых сведений, сложных запросов из 1с с ключами и без,
- выгрузка множества таблиц значений в одном обмене с легким добавлений источников.
Реализация
За основу возьмем и переделаем стандартную процедуру модуля Битрикс "Б_ПроцедурыИФункцииВыгрузкиДанныхНаСайт.ВыгрузитьПользовательскиеСправочникиXDTO"
Эта процедура выгружает любые пользовательские справочники и их автоматически загружает их Highload-блоки в Битрикс, она наиболее близка к нашим целям.
Главная проблема ее использования для отправки произвольных таблиц - это необходимость xmlid-ключа для каждой строки передаваемых в Битрикс данных, этот ключ нужен для обновления уже существующих данных. А у результатов запросов ключа может не быть или быть сразу несколько.
Врезки
можете сделать ее через расширение или через вставку куска кода
Процедура для старых УТ10, КА\ЕРП1.3, БП2 и модулей обмена конфигураций на обычных формах:
Процедура для УНФ 1.6-3.0, УТ11, КА\ЕРП2.5, БП3, модулей обмена конфигураций на управляемых формах:
////infostart.ru/profile/540760/, hands.center просим при любом копировании сохранять авторство идеи и решения
Процедура ВыгрузитьПользовательскиеЗапросыXDTOКастом(ПараметрыОбмена,МассивДанных, Операция)
лКоличествоСтрок = МассивДанных.Количество();
КоличествоПакетов = ?(лКоличествоСтрок > 0, МассивДанных[лКоличествоСтрок - 1].Пакет, 0);
Пакет = КоличествоПакетов + 1;
Б_ОбменССайтомСервер.СообщитьПодробно("Начало выгрузки пакета " + Строка(Пакет) + " пользовательскиих запросов", ПараметрыОбмена);
ПараметрыОбмена.МояФабрикаXDTO = СоздатьФабрикуXDTO(ПараметрыОбмена.ФайлСхемы);
XDTOКоммерческаяИнформацияТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "КоммерческаяИнформация");
XDTOКоммерческаяИнформация = Б_ОбменССайтомСервер.ПолучитьXDTOКоммерческаяИнформация(ПараметрыОбмена, XDTOКоммерческаяИнформацияТип);
XDTOПользовательскиеСправочникиТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "ПользовательскиеСправочники", Истина, XDTOКоммерческаяИнформацияТип);
XDTOСправочникТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "Справочник");
XDTOЭлементыСправочникаТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "ЭлементыСправочника", Истина, XDTOСправочникТип);
XDTOЭлементСправочникаТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "ЭлементСправочника");
ТипpЗначенийРеквизитов = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "ЗначенияРеквизитов", Истина, XDTOЭлементСправочникаТип);
ТипpЗначенияРеквизита = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "ЗначениеРеквизита");
XDTOРеквизитыТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "Реквизиты", Истина, XDTOСправочникТип);
XDTOРеквизитТип = Б_ОбменССайтомСервер.ПолучениеТипОбъектаXDTO(ПараметрыОбмена, "Реквизит", Истина, XDTOРеквизитыТип);
//результирующая таблица выгружаемых данных
ВыгружаемыеСправочники = Новый ТаблицаЗначений;
ВыгружаемыеСправочники.Колонки.Добавить("Справочник");
ВыгружаемыеСправочники.Колонки.Добавить("Таблица");
//поля с синонимами _XMLID будут определены как ключи для записи
//Абсолютно любой запрос, любой сложности
ПользовательскиеЗапросы = Новый Запрос;
ПользовательскиеЗапросы.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100
| Контрагенты.Ссылка КАК Контрагент_XMLID,
| Контрагенты.Наименование КАК Наименование,
| Контрагенты.Наименование КАК НаименованиеЛат,
| Контрагенты.ИНН КАК КонтрагентИНН,
| Контрагенты.КПП КАК КонтрагентКПП,
| РасчетыСПокупателями.Договор КАК Договор_XMLID,
| РасчетыСПокупателями.СуммаОстаток КАК СуммаОстаток,
| РасчетыСПокупателями.СуммаРегОстаток КАК СуммаОстатокРег
|ИЗ
| РегистрНакопления.РасчетыСПокупателями.Остатки КАК РасчетыСПокупателями
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО РасчетыСПокупателями.Контрагент = Контрагенты.Ссылка";
ТаблицаРезультат = ПользовательскиеЗапросы.Выполнить().Выгрузить();
//Если необходимо делаем постобработку полученных данных
Для каждого стр из ТаблицаРезультат цикл
//убираем форматирование числа СК
стр.НаименованиеЛат = СтроковыеФункцииКлиентСервер.СтрокаЛатиницей(стр.Наименование);
КонецЦикла;
НСтр = ВыгружаемыеСправочники.Добавить();
//определяем имя HiLoad блока в битрикс
//Имя сущности не может заканчиваться на "Table", в то же время в вызовах API этот постфикс добавляется автоматически
НСтр.Справочник = "DemoCustomerBalances";
НСтр.Таблица = ТаблицаРезультат.Скопировать();
//поля с синонимами _XMLID будут определены как ключи для записи
//Абсолютно любой запрос, любой сложности
ПользовательскиеЗапросы = Новый Запрос;
ПользовательскиеЗапросы.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100
| ЗапасыОстатки.Номенклатура КАК Номенклатура_XMLID,
| ЗапасыОстатки.Характеристика КАК Характеристика_XMLID,
| ЗапасыОстатки.СтруктурнаяЕдиница КАК Склад_XMLID,
| ЗапасыОстатки.СуммаОстаток КАК СуммаОстаток
|ИЗ
| РегистрНакопления.Запасы.Остатки(, ) КАК ЗапасыОстатки";
ТаблицаРезультат = ПользовательскиеЗапросы.Выполнить().Выгрузить();
НСтр = ВыгружаемыеСправочники.Добавить();
//определяем имя HiLoad блока в битрикс
//Имя сущности не может заканчиваться на "Table", в то же время в вызовах API этот постфикс добавляется автоматически
НСтр.Справочник = "DemoGoodsBalances";
НСтр.Таблица = ТаблицаРезультат.Скопировать();
//поля с синонимами _XMLID будут определены как ключи для записи
//Абсолютно любой запрос, любой сложности
ПользовательскиеЗапросы = Новый Запрос;
ПользовательскиеЗапросы.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100
| РасчетыСКонтрагентамиОбороты.Организация КАК Организация_XMLID,
| РасчетыСКонтрагентамиОбороты.Контрагент КАК Контрагент_XMLID,
| РасчетыСКонтрагентамиОбороты.Договор КАК Договор_XMLID,
| ПРЕДСТАВЛЕНИЕ(РасчетыСКонтрагентамиОбороты.Регистратор) КАК Документ,
| РасчетыСКонтрагентамиОбороты.СуммаПриход КАК СуммаПриход,
| РасчетыСКонтрагентамиОбороты.СуммаРасход КАК СуммаРасход
|ИЗ
| РегистрНакопления.РасчетыСПокупателями.Обороты(, , Регистратор, ) КАК РасчетыСКонтрагентамиОбороты";
ТаблицаРезультат = ПользовательскиеЗапросы.Выполнить().Выгрузить();
НСтр = ВыгружаемыеСправочники.Добавить();
//определяем имя HiLoad блока в битрикс
//Имя сущности не может заканчиваться на "Table", в то же время в вызовах API этот постфикс добавляется автоматически
НСтр.Справочник = "DemoCustomerDocuments";
НСтр.Таблица = ТаблицаРезультат.Скопировать();
//поля с синонимами _XMLID будут определены как ключи для записи
//просто произвольная таблица
ТаблицаРезультат = новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Ключ1__XMLID");
ТаблицаРезультат.Колонки.Добавить("Ключ2__XMLID");
ТаблицаРезультат.Колонки.Добавить("Данные");
Для ключ1 = 1 по 100 цикл
Для ключ2 = 1 по 100 цикл
стрТаблицаРезультат = ТаблицаРезультат.Добавить();
стрТаблицаРезультат.Ключ1__XMLID = Строка(ключ1);
стрТаблицаРезультат.Ключ2__XMLID = Строка(ключ2);
стрТаблицаРезультат.Данные = ключ1 * ключ2;
КонецЦикла;
КонецЦикла;
НСтр = ВыгружаемыеСправочники.Добавить();
//определяем имя HiLoad блока в битрикс
//Имя сущности не может заканчиваться на "Table", в то же время в вызовах API этот постфикс добавляется автоматически
НСтр.Справочник = "DemoMultiplication";//определяем имя HiLoad блока в битрикс
НСтр.Таблица = ТаблицаРезультат.Скопировать();
//Обработка и отправка запросов в битрикс
Итератор = 0;
XDTOПользовательскиеСправочники = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOПользовательскиеСправочникиТип);
Если (ПараметрыОбмена.ВыгружатьТолькоИзменения И НЕ ПараметрыОбмена.ВыполнятьПолнуюВыгрузкуПринудительно) ИЛИ (ПараметрыОбмена.КоличествоЭлементовСправочникаВПакете <> 0) тогда
XDTOПользовательскиеСправочники.СодержитТолькоИзменения = XMLСтрока(Истина);
Иначе
XDTOПользовательскиеСправочники.СодержитТолькоИзменения = XMLСтрока(Ложь);
КонецЕсли;
Для каждого ТекСпр из ВыгружаемыеСправочники Цикл
ТаблицаЗапросов = ТекСпр.Таблица;
лЕстьЭлементыСправочника = Ложь;
XDTOСправочник = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOСправочникТип);
XDTOСправочник.Ид = ТекСпр.Справочник;
XDTOСправочник.Наименование = ТекСпр.Справочник;
лРеквезитыСпр = ТаблицаЗапросов.Колонки;
XDTOРеквизитыСправочника = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOРеквизитыТип);
МассивКолонокСсылок = Новый Массив;
Для каждого ТекРек из лРеквезитыСпр Цикл
//собираем названия полей для создания ключей
Если Найти(ТекРек.Имя,"_XMLID") тогда
МассивКолонокСсылок.Добавить(ТекРек.Имя);
КонецЕсли;
XDTOРеквизитСправочника = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOРеквизитТип);
XDTOРеквизитСправочника.Ид = ТекРек.Имя;
лТипыРеквизита = ТекРек.ТипЗначения.Типы();
XDTOРеквизитСправочника.Наименование = ТекРек.Имя;
//определяем типы выгружаемых данных
Если лТипыРеквизита.Количество() > 0 тогда
лТипРеквизита = лТипыРеквизита[0];
Если лТипРеквизита = Тип("Число") тогда
XDTOРеквизитСправочника.ТипЗначений = "Число";
ИначеЕсли лТипРеквизита = Тип("Дата") тогда
XDTOРеквизитСправочника.ТипЗначений = "Дата";
ИначеЕсли лТипРеквизита = Тип("Булево") тогда
XDTOРеквизитСправочника.ТипЗначений = "Булево";
Иначе
XDTOРеквизитСправочника.ТипЗначений = "Строка";
КонецЕсли;
Иначе
XDTOРеквизитСправочника.ТипЗначений = "Строка";
КонецЕсли;
XDTOРеквизитыСправочника.Реквизит.Добавить(XDTOРеквизитСправочника);
КонецЦикла;
XDTOСправочник.Реквизиты = XDTOРеквизитыСправочника;
XDTOЭлементыСправочника = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOЭлементыСправочникаТип);
// заполняем данные из отправляемых таблиц значений
Для каждого ТекЭлСпр из ТаблицаЗапросов Цикл
лЕстьЭлементыСправочника = Истина;
Итератор = Итератор + 1;
XDTOЭлементСправочника = ПараметрыОбмена.МояФабрикаXDTO.Создать(XDTOЭлементСправочникаТип);
//генерируем ключ записи для битрикс
Наименование = "";
если МассивКолонокСсылок.Количество() = 0 тогда
Б_ОбменССайтомСервер.СообщитьПодробно("Выгрузка запроса " + Строка(ТекСпр.Справочник) + " пропущена. Нет ключевой колонки.", ПараметрыОбмена);
продолжить;
Иначеесли МассивКолонокСсылок.Количество() = 1 тогда
//Если в отправляемой таблице только одна ключевая колонка то мы ее идентификатор отправляем как есть
КлючЗаписи = XMLСтрока(ТекЭлСпр[МассивКолонокСсылок[0]]);
Наименование = Строка(ТекЭлСпр[МассивКолонокСсылок[0]]);
Иначеесли МассивКолонокСсылок.Количество() > 1 тогда
//Если в отправляемой таблице несколько ключевых колонок то мы генерируем их совокупный идентификатор через md5
//такой подход позволяем получить один постоянный ключ из нескольких идентификаторов
Хеш = Новый ХешированиеДанных(ХешФункция.MD5);
Для каждого КолонкаСсылки из МассивКолонокСсылок цикл
Хеш.Добавить(XMLСтрока(ТекЭлСпр[КолонкаСсылки]));
Наименование = Наименование + "#" + СокрЛП(Строка(ТекЭлСпр[КолонкаСсылки]));
КонецЦикла;
КлючЗаписи = СтрЗаменить(строка(Хеш.ХешСумма)," ", "");
КонецЕсли;
XDTOЭлементСправочника.Ид = КлючЗаписи;
//принудительное обновление записей в битрикс пустем передачи в версию дату в формате Unix Timestamp
XDTOЭлементСправочника.НомерВерсии = Формат(Число(ТекущаяДата()-Дата("19700101")),"ЧН=0; ЧГ=0");
ЗначенияРеквизитов = ПараметрыОбмена.МояФабрикаXDTO.Создать(ТипpЗначенийРеквизитов);
//Предопределяем наименование
Б_ОбменССайтомСервер.ДобавитьЗначениеРеквизитаXDTO(ПараметрыОбмена, ЗначенияРеквизитов, ТипpЗначенияРеквизита, "Наименование", Наименование);
Для каждого ТекРек из лРеквезитыСпр Цикл
лЗначРек = ТекЭлСпр[ТекРек.Имя];
Если ЗначениеЗаполнено(лЗначРек) тогда
Если МассивКолонокСсылок.Найти(ТекРек.Имя) <> Неопределено тогда
лЗначВХМЛ = XMLСтрока(лЗначРек);
Иначе
лЗначВХМЛ = Строка(лЗначРек);
КонецЕсли;
Б_ОбменССайтомСервер.ДобавитьЗначениеРеквизитаXDTO(ПараметрыОбмена, ЗначенияРеквизитов, ТипpЗначенияРеквизита, ТекРек.Имя, лЗначВХМЛ);
Иначе
Б_ОбменССайтомСервер.ДобавитьЗначениеРеквизитаXDTO(ПараметрыОбмена, ЗначенияРеквизитов, ТипpЗначенияРеквизита, ТекРек.Имя, "");
КонецЕсли;
КонецЦикла;
XDTOЭлементСправочника.ЗначенияРеквизитов = ЗначенияРеквизитов;
XDTOЭлементыСправочника.ЭлементСправочника.Добавить(XDTOЭлементСправочника);
КонецЦикла;
Если лЕстьЭлементыСправочника тогда
XDTOСправочник.ЭлементыСправочника = XDTOЭлементыСправочника;
XDTOПользовательскиеСправочники.Справочник.Добавить(XDTOСправочник);
КонецЕсли;
КонецЦикла;
XDTOКоммерческаяИнформация.ПользовательскиеСправочники = XDTOПользовательскиеСправочники;
ИмяКаталога = Б_ОбщиеПроцедурыИФункцииСервер.ПолучитьПутьДляПлатформы(ПараметрыОбмена.ПлатформаWindows, ПараметрыОбмена.КаталогНаДиске + "\references\" + Строка(Пакет));
Успешно = ЗаписьИОтправкаXMLДанныхНаСайт(ПараметрыОбмена, XDTOКоммерческаяИнформация, ИмяКаталога, "references", "reference", Пакет);
Если Успешно Или НЕ ПараметрыОбмена.ВыгружатьНаСайт тогда
Б_ОбменССайтомСервер.СообщитьПодробно("Выгрузка пакета " + Строка(Пакет) + " пользовательскиих запросов завершена. Выгружено " + Строка(Итератор) + " элементов.", ПараметрыОбмена);
Иначе
Б_ОбменССайтомСервер.СообщитьПодробно("Выгрузка пакета " + Строка(Пакет) + " пользовательскиих запросов НЕ завершена или завершена с ошибками. НЕ Выгружено " + Строка(Итератор) + " элементов.", ПараметрыОбмена);
КонецЕсли;
КонецПроцедуры
////infostart.ru/profile/540760/, hands.center просим при любом копировании сохранять авторство идеи и решения
Результаты
При обмене вы увидите сообщения о создание новых справочников Highload с их идентификатором в Битрикс
После обмена вы увидите в вашем Битрикс новые Highload-блоки с указанными вами именами.
с интересом почитаю ваши комментарии )