Преобразование правил обмена "на лету"

Изменение правил обмена из кода конфигурации, без необходимости ручного обновления (внесения изменений/доработок) типовых правил при обновлении на новые релизы.

Буду краток. Ситуация:

Заказчику нужно дописать типовые правила обмена.

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

 

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

 

Альтернатива (решение): написать код, который при нажати кнопки сам модифицирует типовые правила. Таким образом, для поддержания актуальности правил при очередном обновлении на релиз достаточно нажать эту кнопку.

 

Как это работает: любая типовая конфигурация — Синхронизация — Настройка синхронизации — Загрузить правила — «Из конфигурации» — кнопка «Записать и закрыть». В момент нажатия в регистр загружаются типовые правила обмена и далее используются как актуальные.

Данный код загрузки типовых правил мы и будем менять. В результате при нажатии на кнопку «Записать и закрыть» мы получим типовые правила обмена, но модифицированные согласно заложенному коду.

 

Реализация: Конфигуратор — обработки — КонвертацияОбъектовИнформационныхБаз — модуль объекта — процедура ЗагрузитьПравилаОбмена():

 

Процедура ЗагрузитьПравилаОбмена(Источник="",
ТипИсточника="XMLФайл",
СтрокаСообщенияОбОшибке = "",
ЗагружатьТолькоЗаголовокПравил = Ложь
) Экспорт
//...

СоздатьСтруктуруКонвертации();

ТаблицаПравилКонвертацииСвойств = Новый ТаблицаЗначений;
ИнициализацияТаблицыПравилКонвертацииСвойств(ТаблицаПравилКонвертацииСвойств);

// Возможно выбраны встроенные правила обмена (один из макетов).

ИмяВременногоФайлаПравилОбмена = "";
Если ПустаяСтрока(Источник) Тогда

Источник = ИмяФайлаПравилОбмена;

КонецЕсли;


//+изменения
Если ЭтотОбъект.ИмяПланаОбменаВРО = "ОбменУправлениеНебольшойФирмойБухгалтерия30" Тогда
ПреобразоватьПравилаОбмена(Источник);
КонецЕсли;
//-изменения

Если ТипИсточника="XMLФайл" Тогда

//...
КонецПроцедуры
 

Конструцией //… обозначен некий участок кода, который в данном листинге пропущен.

Изменения в типовом коде помечены //+изменения и //-изменения

Комментарий по коду: после того, как мы проинициализировали типовые правила (Источник = ИмяФайлаПравилОбмена), мы эти правила модифицируем: ПреобразоватьПравилаОбмена(Источник)

Теперь код процедуры ПреобразоватьПравилаОбмена:


//+изменения
Процедура ПреобразоватьПравилаОбмена(Источник)
// Чтение XML в дерево
Чтение = Новый ЧтениеXML;
ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,Ложь,,,Ложь);
Чтение.ОткрытьФайл(Источник,ПараметрыЧтения);

СтруктураУзла = Новый ДеревоЗначений;
СтруктураУзла.Колонки.Добавить("Имя");
СтруктураУзла.Колонки.Добавить("Значение");
СтруктураУзла.Колонки.Добавить("Атрибуты");
СтруктураУзла.Колонки.Добавить("Тип");

СтрокаУзла = СтруктураУзла.Строки.Добавить();
СтрокаУзла.Значение = СокрЛП(Источник);
СтрокаУзла.Атрибуты = Новый Структура;

ПрочитатьОбъектXMLВДерево(Чтение,СтрокаУзла);

Чтение.Закрыть();



// !!!
ПравилоПослеЗагрузкиДанных = СтруктураУзла.Строки.Найти("ПослеЗагрузкиДанных","Имя",Истина);
Если ПравилоПослеЗагрузкиДанных <> Неопределено Тогда
ПравилоПослеЗагрузкиДанныхСПроведением = "Объект.Записать(РежимЗаписиДокумента.Проведение);";
ПравилоПослеЗагрузкиДанныхБезПроведения = "//Объект.Записать(РежимЗаписиДокумента.ПравилоПослеЗагрузкиДанныхСПроведением); //-изменения";
ПравилоПослеЗагрузкиДанных.Значение = СтрЗаменить(ПравилоПослеЗагрузкиДанных.Значение,ПравилоПослеЗагрузкиДанныхСПроведением,ПравилоПослеЗагрузкиДанныхБезПроведения);
КонецЕсли;

// !!!
ПравилаКонвертацииОбъектов = СтруктураУзла.Строки.Найти("ПравилаКонвертацииОбъектов","Имя",Истина);
ПреобразоватьСтрокиПравил(ПравилаКонвертацииОбъектов);



// Запись XML из дерева
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла);
ПрочитатьОбъектXMLИзДерева(ЗаписьXML,СтруктураУзла.Строки[0]);
ЗаписьXML.Закрыть();

Источник = ИмяВременногоФайла;
КонецПроцедуры // ПреобразоватьПравилаОбмена()
Процедура ПреобразоватьСтрокиПравил(ПравилаКонвертацииОбъектов)
Если Ложь Тогда ПравилаКонвертацииОбъектов = Новый ДеревоЗначений; КонецЕсли;

Для Каждого ПравилоКО Из ПравилаКонвертацииОбъектов.Строки Цикл
Источник = ПравилоКО.Строки.Найти("Источник","Имя");

Если Источник <> Неопределено Тогда
// Номенклатуру синхронизируем по наименованию
Если Источник.Значение = "СправочникСсылка.Номенклатура" Тогда
Для Каждого РеквизитОбъекта Из ПравилоКО.Строки.Найти("Свойства","Имя").Строки Цикл
ИмяРеквизита = РеквизитОбъекта.Строки.Найти("Источник","Имя");
Если ИмяРеквизита.Атрибуты.Имя <> "Наименование" И ИмяРеквизита.Атрибуты.Имя <> "ЭтоГруппа" Тогда
РеквизитОбъекта.Атрибуты.Удалить("Поиск");
КонецЕсли;
КонецЦикла;
КонецЕсли;

// Банковские счета
Если Источник.Значение = "СправочникСсылка.БанковскиеСчета" Тогда
ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Найти("ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли","Имя");
Если ПравилоПоискПоПолямПоиска = Неопределено Тогда
ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Добавить();
ПравилоПоискПоПолямПоиска.Имя = "ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли";
ПравилоПоискПоПолямПоиска.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоПоискПоПолямПоиска.Значение = "true";
КонецЕсли;

Если Источник.Значение = "СправочникСсылка.ДоговорыКонтрагентов" Тогда
ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Найти("ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли","Имя");
Если ПравилоПоискПоПолямПоиска = Неопределено Тогда
ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Добавить();
ПравилоПоискПоПолямПоиска.Имя = "ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли";
ПравилоПоискПоПолямПоиска.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоПоискПоПолямПоиска.Значение = "true";

Для Каждого РеквизитОбъекта Из ПравилоКО.Строки.Найти("Свойства","Имя").Строки Цикл
ИмяРеквизита = РеквизитОбъекта.Строки.Найти("Источник","Имя");
Если ИмяРеквизита.Атрибуты.Имя = "Организация" Тогда
РеквизитОбъекта.Атрибуты.Удалить("Поиск");
КонецЕсли;
КонецЦикла;
КонецЕсли;

// Контрагентов синхронизируем по ИНН/КПП
Если Источник.Значение = "СправочникСсылка.Контрагенты" Тогда
//ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Найти("СинхронизироватьПоИдентификатору","Имя");
//Если ПравилоПоискПоПолямПоиска = Неопределено Тогда
// ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Добавить();
// ПравилоПоискПоПолямПоиска.Имя = "СинхронизироватьПоИдентификатору";
// ПравилоПоискПоПолямПоиска.Тип = ТипУзлаXML.Текст;
//КонецЕсли;
//ПравилоПоискПоПолямПоиска.Значение = "false";

ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Найти("ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли","Имя");
Если ПравилоПоискПоПолямПоиска = Неопределено Тогда
ПравилоПоискПоПолямПоиска = ПравилоКО.Строки.Добавить();
ПравилоПоискПоПолямПоиска.Имя = "ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли";
ПравилоПоискПоПолямПоиска.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоПоискПоПолямПоиска.Значение = "true";

ПравилоНеЗамещать = ПравилоКО.Строки.Найти("НеЗамещать","Имя");
Если ПравилоНеЗамещать = Неопределено Тогда
ПравилоНеЗамещать = ПравилоКО.Строки.Вставить(0);
ПравилоНеЗамещать.Имя = "НеЗамещать";
ПравилоНеЗамещать.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоНеЗамещать.Значение = "true";

ПравилоПоляПоиска = ПравилоКО.Строки.Найти("ПоследовательностьПолейПоиска","Имя");
Если ПравилоПоляПоиска = Неопределено Тогда
ПравилоПоляПоиска = ПравилоКО.Строки.Добавить();
ПравилоПоляПоиска.Имя = "ПоследовательностьПолейПоиска";
ПравилоПоляПоиска.Тип = ТипУзлаXML.Текст;
КонецЕсли;

ПравилоПоляПоиска.Значение = "//+изменения
|Если НомерВариантаПоиска = 1 Тогда
| Если СвойстваПоиска[""ЭтоГруппа""] = Истина Тогда
| СтрокаИменСвойствПоиска = ""Наименование,ЭтоГруппа"";
| Иначе
| Если ЗначениеЗаполнено(СвойстваПоиска[""ИНН""]) Тогда
| СтрокаИменСвойствПоиска = ""ИНН,КПП,ЭтоГруппа"";
| Иначе
| СтрокаИменСвойствПоиска = ""Наименование,ИНН,КПП,ЭтоГруппа"";
| КонецЕсли;
| КонецЕсли;
|КонецЕсли;
|//-изменения";
КонецЕсли;


// Документы выгружаем непроведенными
Если Найти(Источник.Значение,"ДокументСсылка.") > 0 Тогда

Для Каждого РеквизитОбъекта Из ПравилоКО.Строки.Найти("Свойства","Имя").Строки Цикл
ИмяРеквизита = РеквизитОбъекта.Строки.Найти("Источник","Имя");
Если ИмяРеквизита.Атрибуты.Имя = "Проведен" Тогда
ПравилоПередВыгрузкой = РеквизитОбъекта.Строки.Добавить();
ПравилоПередВыгрузкой.Имя = "ПередВыгрузкой";
ПравилоПередВыгрузкой.Значение = "Значение = Ложь; //+изменения";
ПравилоПередВыгрузкой.Тип = ТипУзлаXML.Текст;

//ПравилоНеЗамещать = РеквизитОбъекта.Строки.Найти("НеЗамещать","Имя");
//Если ПравилоНеЗамещать = Неопределено Тогда
// ПравилоНеЗамещать = РеквизитОбъекта.Строки.Вставить(0);
// ПравилоНеЗамещать.Имя = "НеЗамещать";
// ПравилоНеЗамещать.Тип = ТипУзлаXML.Текст;
//КонецЕсли;
//ПравилоНеЗамещать.Значение = "true";
КонецЕсли;

ИмяРеквизита = РеквизитОбъекта.Строки.Найти("Приемник","Имя");
Если ИмяРеквизита.Атрибуты.Имя = "СпособЗачетаАвансов" Тогда
ПравилоПередВыгрузкой = РеквизитОбъекта.Строки.Найти("ПередВыгрузкой","Имя");
Если ПравилоПередВыгрузкой <> Неопределено Тогда
Если Найти(ПравилоПередВыгрузкой.Значение,"Выражение = ""Перечисления.СпособыЗачетаАвансов.") > 0 Тогда
ПравилоПередВыгрузкой.Значение = ПравилоПередВыгрузкой.Значение + "
|
|Выражение = ""Перечисления.СпособыЗачетаАвансов.Автоматически""; //+изменения";
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;

Если Найти(Источник.Значение,"ДокументСсылка.ПоступлениеНаСчет") > 0 Тогда
ПравилоПередВыгрузкой = ПравилоКО.Строки.Найти("ПередВыгрузкой","Имя");
Если ПравилоПередВыгрузкой = Неопределено Тогда
ПравилоПередВыгрузкой = ПравилоКО.Строки.Добавить();
ПравилоПередВыгрузкой.Имя = "ПередВыгрузкой";
ПравилоПередВыгрузкой.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоПередВыгрузкой.Значение = "Отказ = Истина;";
КонецЕсли;
Если Найти(Источник.Значение,"ДокументСсылка.РасходнаяНакладная") > 0 Тогда
Для Каждого РеквизитОбъекта Из ПравилоКО.Строки.Найти("Свойства","Имя").Строки Цикл
ИмяРеквизита = РеквизитОбъекта.Строки.Найти("Источник","Имя");
Если ИмяРеквизита.Атрибуты.Имя = "Предоплата" Тогда
ПравилоПередВыгрузкой = РеквизитОбъекта.Строки.Найти("ПередВыгрузкой","Имя");
Если ПравилоПередВыгрузкой = Неопределено Тогда
ПравилоПередВыгрузкой = РеквизитОбъекта.Строки.Добавить();
ПравилоПередВыгрузкой.Имя = "ПередВыгрузкой";
ПравилоПередВыгрузкой.Тип = ТипУзлаXML.Текст;
КонецЕсли;
ПравилоПередВыгрузкой.Значение = ПравилоПередВыгрузкой.Значение + "
|
|Отказ = Истина; //+изменения";
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;

ПреобразоватьСтрокиПравил(ПравилоКО);
КонецЦикла;
КонецПроцедуры // ПреобразоватьСтрокиПравил()

Функция ПрочитатьОбъектXMLВДерево(ЭлементЧтениеXML, Дерево)
текСтрока = Дерево;
Пока ЭлементЧтениеXML.Прочитать() Цикл

Если ЭлементЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

НоваяСтрока = текСтрока.Строки.Добавить();
НоваяСтрока.Имя = ЭлементЧтениеXML.Имя;
Атрибуты = Новый Структура;
Пока ЭлементЧтениеXML.ПрочитатьАтрибут() Цикл

Атрибуты.Вставить(ЭлементЧтениеXML.Имя, ЭлементЧтениеXML.Значение);

КонецЦикла;
НоваяСтрока.Атрибуты = Атрибуты;
НоваяСтрока.Тип = ТипУзлаXML.НачалоЭлемента;
ПрочитатьОбъектXMLВДерево(ЭлементЧтениеXML, НоваяСтрока);

ИначеЕсли ЭлементЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда

текСтрока.Значение = ЭлементЧтениеXML.Значение;
текСтрока.Тип = ТипУзлаXML.Текст;


ИначеЕсли ЭлементЧтениеXML.ТипУзла = ТипУзлаXML.Комментарий Тогда

НоваяСтрока = текСтрока.Строки.Добавить();
Атрибуты = Новый Структура;
НоваяСтрока.Значение = ЭлементЧтениеXML.Значение;
НоваяСтрока.Атрибуты = Атрибуты;
НоваяСтрока.Тип = ТипУзлаXML.Комментарий;

ИначеЕсли ЭлементЧтениеXML.ТипУзла = ТипУзлаXML.ОбъявлениеXML Тогда

НоваяСтрока = текСтрока.Строки.Добавить();
Атрибуты = Новый Структура;
НоваяСтрока.Значение = "<?xml version=""" + ЭлементЧтениеXML.ВерсияXML + """" + ?(ПустаяСтрока(ЭлементЧтениеXML.КодировкаXML), "", " encoding=""" + ЭлементЧтениеXML.КодировкаXML + """") + " ?>";
НоваяСтрока.Атрибуты = Атрибуты;
НоваяСтрока.Тип = ТипУзлаXML.ОбъявлениеXML;
ПрочитатьОбъектXMLВДерево(ЭлементЧтениеXML, НоваяСтрока);


ИначеЕсли ЭлементЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда

Прервать;

КонецЕсли;

КонецЦикла;

Возврат Дерево;

КонецФункции
Процедура ПрочитатьОбъектXMLИзДерева(ЗаписьXML, Дерево) Экспорт
Если Дерево.Тип = ТипУзлаXML.ОбъявлениеXML Тогда
ЗаписьXML.ЗаписатьОбъявлениеXML();

Для Каждого текСтрока Из Дерево.Строки Цикл
ПрочитатьОбъектXMLИзДерева(ЗаписьXML, текСтрока);
КонецЦикла;

ИначеЕсли Дерево.Тип = ТипУзлаXML.Комментарий Тогда
ЗаписьXML.ЗаписатьКомментарий(Дерево.Значение);
Для Каждого текСтрока Из Дерево.Строки Цикл
ПрочитатьОбъектXMLИзДерева(ЗаписьXML, текСтрока);
КонецЦикла;

ИначеЕсли Дерево.Тип = ТипУзлаXML.НачалоЭлемента Тогда
ЗаписьXML.ЗаписатьНачалоЭлемента(Дерево.Имя);
Если ЗначениеЗаполнено(Дерево.Атрибуты) Тогда
Для Каждого текАтрибут Из Дерево.Атрибуты Цикл
ЗаписьXML.ЗаписатьАтрибут(текАтрибут.Ключ, текАтрибут.Значение);
КонецЦикла;
КонецЕсли;

Для Каждого текСтрока Из Дерево.Строки Цикл
ПрочитатьОбъектXMLИзДерева(ЗаписьXML, текСтрока);
КонецЦикла;

ЗаписьXML.ЗаписатьКонецЭлемента();

ИначеЕсли Дерево.Тип = ТипУзлаXML.Текст Тогда
ЗаписьXML.ЗаписатьНачалоЭлемента(Дерево.Имя);
Если ЗначениеЗаполнено(Дерево.Атрибуты) Тогда
Для Каждого текАтрибут Из Дерево.Атрибуты Цикл
ЗаписьXML.ЗаписатьАтрибут(текАтрибут.Ключ, текАтрибут.Значение);
КонецЦикла;
КонецЕсли;
ЗаписьXML.ЗаписатьТекст(Дерево.Значение);
ЗаписьXML.ЗаписатьКонецЭлемента();

Для Каждого текСтрока Из Дерево.Строки Цикл
ПрочитатьОбъектXMLИзДерева(ЗаписьXML, текСтрока);
КонецЦикла;

Иначе
Для Каждого текСтрока Из Дерево.Строки Цикл
ПрочитатьОбъектXMLИзДерева(ЗаписьXML, текСтрока);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
//-изменения




Комментарий по коду:

Приведен полностью листинг одного реально модифицированного набора правил. Вначале производится чтение правил и помещение их в дерево значений, затем дерево значений преобразуется (добавляются/удаляются/модифицируются правила), затем дерево обратно собирается в xml и выдается обратно в вызывающую функцию.

В листинге есть примеры:

— как в справочнике Номенклатура оставить синхронизацию только по Наименованию и Группе (см. РеквизитОбъекта.Атрибуты.Удалить(«Поиск»))

— как включить синхронизацию по полям поиска, если по внутреннему идентификатору объект не найден (см. ПравилоКО.Строки.Найти(«ПродолжитьПоискПоПолямПоискаЕслиПоИдентификаторуНеНашли»,»Имя»);)

— как задать алгоритм поиска объектов (см. ПравилоКО.Строки.Найти(«ПоследовательностьПолейПоиска»,»Имя»);)

— как включить режим «Не замещать свойства если объект найден» (см. ПравилоКО.Строки.Найти(«НеЗамещать»,»Имя»);)

— как изменить значения реквизитов выгружаемых объектов (см. Если ИмяРеквизита.Атрибуты.Имя = «Проведен» Тогда )

— и некоторые другие примеры (по аналогии с приведенными выше будет понятно, для чего они предназначены).

 

И последнее: не забывайте, что иногда в конфигурации-приемнике поиск объектов по полям поиска может не сработать, т.к. в конфигурацию-приемник могут быть загружены правила корреспонденции. Если хотим, чтобы заработали правила поиска по полям поиска в этом случае, можем просто загрузить в приемник пустые правила корреспонденции.

8 Comments

  1. insurgut

    А если сущности какой не станет, или названия реквизитов изменятся… Чета как-то, грабли :-/

    Reply
  2. Mopo3

    (1) Этот же вопрос должен возникать, если модифицировать через КД типовые правила обмена и загружать их как актуальные. Поэтому считаю его общим и не имеющим отношения к методике.

    Т.е. если рассуждать абстрактно над таким вопросом, то ответ банально такой: при каждом обновлении релиза нужно проверять доработки типовой 🙂

    Reply
  3. Светлый ум

    Может и грабли, а для расширения кругозора XML — самое то.

    Reply
  4. d@ncer

    Согласен с Мишей 🙂

    Reply
  5. Mopo3

    Каждый имеет право на собственное мнение 🙂

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

    Reply
  6. jan27

    в типовых конфигурациях есть возможность использовать нетиповые правила КД, которые хранятся в базе

    проще использовать такой механизм

    Reply
  7. Mopo3

    (6) Механизм есть, но что вы будете делать когда после обновления релиза и изменения правил поставщиком, система выдаст сообщение что правила устарели и необходимо обновить правила до актуальных?

    Reply
  8. jan27

    (7) обновлю конфигурацию в КД и выгружу правила

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *