Каталог решений - Особенности работы с COM-соединением

Особенности работы с COM-соединением

Особенности работы с COM-соединением

В наличии

О чём и зачем данная статья? Казалось бы, что написано об этом соединении очень много, да и технология устаревшая… И мне так раньше казалось! Однако, столкнувшись с необходимостью на двух проектах подряд работать с этой технологией, стало понятно, что описание трудных ситуаций или отсутствует, или есть только на форумах, что крайне неудобно! Поэтому особенности работы с COM соберу в одном месте, чтоб не искать в будущем.

Категория:

Описание

На моих проектах суть работы с COM-соединением состояла из следующих шагов:

1. Получение большого количества таблиц из базы источника. Данные получаются в основном запросами и программным интрефейсом. 

2. Преобразование ссылочных полей в примитивные типы данных, которые можно прочитать на стороне базы приёмника.

3. Тестирование кода сначала без COM-соединения. Для этого код пишется во внешней обработке для базы источника. 

4. Перенос кода в базу приёмник. Здесь и начинается самое весёлое! Сделать так, чтоб код, который уже отлажен, заработал через COM-соединение.

5. Преобразование таблиц из COM-объекта в тип "Таблица значений".

6. Поиск/создание ссылок. Поиск идёт по ключевым полям, которые на шаге 2 были преобразованы в примитивные типы данных.

7. Загрузка данных.

 

Все проблемы, с которыми я столкнулся, были на шаге 4 и 5. И именно об этих двух шагах моя публикация. 

Но для того, чтоб статья содержала полную информацию, начну с начала. 

 

Этап 1. Подключение к базе.

 

Для подключения нужны 4 параметра:

1. Имя сервера

2. Имя базы

3. Имя пользователя

4. Пароль.

 

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

Обычно задаю эти параметры в коде или в регистре сведений. В коде выглядит так:

//Установим параметры по умолчанию
Сервер = "Srvr=""s-dcr-db02.int""";
ИмяБазы = "Ref=""1c8mskrzn""";
ИмяПользователя = "Usr=""Филатов Павел""";
Пароль = "Pwd=""какой-то пароль""";

ПутьКБазе = Сервер + ";" + ИмяБазы + ";" + ИмяПользователя + ";" + Пароль;

Для подключения в управляемых формах обычно создаю 3 переменных:

 

 

Код кнопки подключиться к базе:

&НаКлиенте
Процедура ПодключитьсяКБазе(Команда)
    ПодключитьсяКБазеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПодключитьсяКБазеНаСервере()

    Если ПодключениеУстановлено Тогда
        Если ЗначениеЗаполнено(АдресCOMСоединения) Тогда
            УдалитьИзВременногоХранилища(АдресCOMСоединения);
        КонецЕсли;

        ПодключениеУстановлено = Ложь;
    Иначе
        Если Не ЗначениеЗаполнено(ПутьКБазе) Тогда
            Возврат;
        КонецЕсли;
		
        COMСоединение = Новый COMObject("V83.COMConnector");
        Попытка
            ПодключениеКБазе = COMСоединение.Connect(ПутьКБазе);
			
            //Если ранее было помещено соединение, удаляем его и помещаем новое
            Если ЗначениеЗаполнено(АдресCOMСоединения) Тогда
                УдалитьИзВременногоХранилища(АдресCOMСоединения);
            Иначе
                АдресCOMСоединения = ПоместитьВоВременноеХранилище(Новый Структура("ПодключениеКБазе", ПодключениеКБазе), Новый УникальныйИдентификатор);
            КонецЕсли;
			
            ПодключениеУстановлено = Истина;
        Исключение
            Инфо = ИнформацияОбОшибке();
            Возврат;
        КонецПопытки;	
    КонецЕсли;

    Элементы.ПодключитьсяКБазе.Пометка = ПодключениеУстановлено;

КонецПроцедуры // ПодключитьсяКБазеУТНаСервере()

Сразу расскажу, зачем кладу во временное хранилище… 

Даже если в модуле описать переменную ПодключениеКБазе как серверную, то это не гарантирует, что при следующем переходе с клиента на сервер она Вас там "будет ждать".

Поэтому она помещается в хранилище, а адрес запоминается в переменной формы "АдресCOMСоединения".

При следующем серверном вызове пишу такой код:

Если ЗначениеЗаполнено(АдресCOMСоединения) Тогда
    СтруктураДанных = ПолучитьИзВременногоХранилища(АдресCOMСоединения);
    ПодключениеКБазе = СтруктураДанных.ПодключениеКБазе;
Иначе 
    Возврат;
КонецЕсли;

//Получаем обработку как объект. К ней в модуле объекта привязаны ключевые переменные с данными.
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
ОбработкаОбъект.ПодключениеКБазе = ПодключениеКБазе;
ОбработкаОбъект.МенеджерВременныхТаблиц = ПодключениеКБазе.NewObject("МенеджерВременныхТаблиц");

И вот теперь с нами везде на сервере будет COM-соединение. Получение данных пишем в модуле объекта (у внешних обработок нет модуля менеджера).

 

Этап 2. Создание объектов через конструктор "Новый".

Почти все объекты создаются без особенностей с помощью конструкции NewObject(). В скобках в кавычках указывается название объекта. 

Примеры наиболее часто используемых конструкций без особенностей:

Запрос = ПодключениеКБазе.NewObject("Запрос");
МенеджерВременныхТаблиц = ПодключениеКБазе.NewObject("МенеджерВременныхТаблиц");

Массив = ПодключениеКБазе.NewObject("Массив");
СписокЗначений = ПодключениеКБазе.NewObject("СписокЗначений");
Структура = ПодключениеКБазе.NewObject("Структура");
Соответствие = ПодключениеКБазе.NewObject("Соответствие");
Таблица = ПодключениеКБазе.NewObject("ТаблицаЗначений");

Особенности возникают при создании полей в таблице и описании их типов. Если без использования соединения код создания полей выглядит так:

//Получение квалификаторов строки, числа
КвалификаторыЧисла15_2 = Новый КвалификаторыЧисла(15, 2);
КвалификаторыСтроки150 = Новый КвалификаторыСтроки(150);

//Через COM-соединение
КвалификаторыЧисла15_2 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 15, 2);
КвалификаторыСтроки150 = ПодключениеКБазе.NewObject("КвалификаторыСтроки", 150);

//Получение описания типов числа
ОписаниеТиповЧисла15_2 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла15_2);

//Через COM-соединение
ОписаниеТиповЧисла15_2 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла15_2);

//Получение описания типов строки
ОписаниеТиповСтроки150 = Новый ОписаниеТипов("Строка",,,, КвалификаторыСтроки150);

//Через COM-соединение
ОписаниеТиповСтроки150 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Строка", КвалификаторыСтроки150);

//Получение описания ссылочных типов
ОписаниеТиповПодразделения = Новый ОписаниеТипов("СправочникСсылка.ПодразделенияОрганизаций");

//Через COM-соединение
ОписаниеТиповПодразделения = ПодключениеКБазе.NewObject("ОписаниеТипов", "СправочникСсылка.ПодразделенияОрганизаций");

Как видно из приведённых примеров, все параметры указываются через запятую, после названия создаваемого объекта.

В обоих проектах, где мне довелось использовать COM-соединение, получалось много таблиц при выгрузке. Все поля таблиц делал типизированными. 

Чтобы не писать много раз создание колонок для каждой таблицы, сделал 2 процедуры с шаблонами, в которых менял только название полей:

1. Функция "ПолучитьСтруктуруОписанияТипов" получает все необходимые мне типы значений, что сильно сокращает количества кода при создании полей. Ниже её код, в т.ч. для COM-соединения:

//Обычный код
Функция ПолучитьСтруктуруОписанияТипов()

    КвалификаторыЧисла15_2 = Новый КвалификаторыЧисла(15, 2);
    КвалификаторыЧисла15_3 = Новый КвалификаторыЧисла(15, 3);
    КвалификаторыЧисла3_0 = Новый КвалификаторыЧисла(3, 0);
    КвалификаторыЧисла6_0 = Новый КвалификаторыЧисла(6, 0); 
    КвалификаторыЧисла10_0 = Новый КвалификаторыЧисла(10, 0); 
    КвалификаторыЧисла10_2 = Новый КвалификаторыЧисла(10, 2); 
    КвалификаторыЧисла6_2 = Новый КвалификаторыЧисла(6, 2); 
    КвалификаторыЧисла8_2 = Новый КвалификаторыЧисла(8, 2); 
	
    КвалификаторыСтроки9 = Новый КвалификаторыСтроки(9);
    КвалификаторыСтроки150 = Новый КвалификаторыСтроки(150);
    КвалификаторыСтроки350 = Новый КвалификаторыСтроки(350);
	
    ОписаниеТиповЧисла15_2 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла15_2);
    ОписаниеТиповЧисла15_3 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла15_3);
    ОписаниеТиповЧисла3_0 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла3_0);
    ОписаниеТиповЧисла6_0 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла6_0);
    ОписаниеТиповЧисла10_0 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла10_0);
    ОписаниеТиповЧисла10_2 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла10_2);
    ОписаниеТиповЧисла6_2 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла6_2);
    ОписаниеТиповЧисла8_2 = Новый ОписаниеТипов("Число",,, КвалификаторыЧисла8_2);
	
    ОписаниеТиповПодразделения = Новый ОписаниеТипов("СправочникСсылка.ПодразделенияОрганизаций");
    ОписаниеТиповСотрудники = Новый ОписаниеТипов("СправочникСсылка.СотрудникиОрганизаций");
    ОписаниеТиповФизическиеЛица = Новый ОписаниеТипов("СправочникСсылка.ФизическиеЛица");
    ОписаниеТиповДолжности = Новый ОписаниеТипов("СправочникСсылка.ДолжностиОрганизаций");
	
    ОписаниеТиповСтроки9 = Новый ОписаниеТипов("Строка",,,, КвалификаторыСтроки9);
    ОписаниеТиповСтроки150 = Новый ОписаниеТипов("Строка",,,, КвалификаторыСтроки150);
    ОписаниеТиповСтроки350 = Новый ОписаниеТипов("Строка",,,, КвалификаторыСтроки350);
	
    ОписаниеТиповБулево = Новый ОписаниеТипов("Булево");
    ОписаниеТиповДаты = Новый ОписаниеТипов("Дата");

    СтруктураОписанияТипов = Новый Структура;
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповПодразделения", ОписаниеТиповПодразделения);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСотрудники", ОписаниеТиповСотрудники);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповФизическиеЛица", ОписаниеТиповФизическиеЛица);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповДолжности", ОписаниеТиповДолжности);

    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла15_2", ОписаниеТиповЧисла15_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла15_3", ОписаниеТиповЧисла15_3);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла3_0", ОписаниеТиповЧисла3_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла6_0", ОписаниеТиповЧисла6_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла10_0", ОписаниеТиповЧисла10_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла10_2", ОписаниеТиповЧисла10_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла6_2", ОписаниеТиповЧисла6_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла8_2", ОписаниеТиповЧисла8_2);
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки9", ОписаниеТиповСтроки9);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки150", ОписаниеТиповСтроки150);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки350", ОписаниеТиповСтроки350);
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповБулево", ОписаниеТиповБулево);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповДаты", ОписаниеТиповДаты);
	
Возврат СтруктураОписанияТипов;

КонецФункции // ПолучитьСтруктуруОписанияТипов()


//Для COM-соединения
Функция ПолучитьСтруктуруОписанияТипов()

    КвалификаторыЧисла15_2 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 15, 2);
    КвалификаторыЧисла15_3 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 15, 3);
    КвалификаторыЧисла3_0 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 3, 0);
    КвалификаторыЧисла6_0 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 6, 0); 
    КвалификаторыЧисла10_0 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 10, 0); 
    КвалификаторыЧисла10_2 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 10, 2); 
    КвалификаторыЧисла6_2 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 6, 2); 
    КвалификаторыЧисла8_2 = ПодключениеКБазе.NewObject("КвалификаторыЧисла", 8, 2); 
	
    КвалификаторыСтроки9 = ПодключениеКБазе.NewObject("КвалификаторыСтроки", 9);
    КвалификаторыСтроки150 = ПодключениеКБазе.NewObject("КвалификаторыСтроки", 150);
    КвалификаторыСтроки350 = ПодключениеКБазе.NewObject("КвалификаторыСтроки", 350);

    ОписаниеТиповЧисла15_2 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла15_2);
    ОписаниеТиповЧисла15_3 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла15_3);
    ОписаниеТиповЧисла3_0 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла3_0);
    ОписаниеТиповЧисла6_0 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла6_0);
    ОписаниеТиповЧисла10_0 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла10_0);
    ОписаниеТиповЧисла10_2 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла10_2);
    ОписаниеТиповЧисла6_2 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла6_2);
    ОписаниеТиповЧисла8_2 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Число", КвалификаторыЧисла8_2);
	
    ОписаниеТиповПодразделения = ПодключениеКБазе.NewObject("ОписаниеТипов", "СправочникСсылка.ПодразделенияОрганизаций");
	
    ОписаниеТиповСтроки9 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Строка", КвалификаторыСтроки9);
    ОписаниеТиповСтроки150 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Строка", КвалификаторыСтроки150);
    ОписаниеТиповСтроки350 = ПодключениеКБазе.NewObject("ОписаниеТипов", "Строка", КвалификаторыСтроки350);
	
    ОписаниеТиповБулево = ПодключениеКБазе.NewObject("ОписаниеТипов", "Булево");
    ОписаниеТиповДаты = ПодключениеКБазе.NewObject("ОписаниеТипов", "Дата");

    СтруктураОписанияТипов = ПодключениеКБазе.NewObject("Структура");
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповПодразделения", ОписаниеТиповПодразделения);

    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла15_2", ОписаниеТиповЧисла15_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла15_3", ОписаниеТиповЧисла15_3);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла3_0", ОписаниеТиповЧисла3_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла6_0", ОписаниеТиповЧисла6_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла10_0", ОписаниеТиповЧисла10_0);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла10_2", ОписаниеТиповЧисла10_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла6_2", ОписаниеТиповЧисла6_2);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповЧисла8_2", ОписаниеТиповЧисла8_2);
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки9", ОписаниеТиповСтроки9);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки150", ОписаниеТиповСтроки150);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповСтроки350", ОписаниеТиповСтроки350);
	
    СтруктураОписанияТипов.Вставить("ОписаниеТиповБулево", ОписаниеТиповБулево);
    СтруктураОписанияТипов.Вставить("ОписаниеТиповДаты", ОписаниеТиповДаты);
	
    Возврат СтруктураОписанияТипов;

КонецФункции // ПолучитьСтруктуруОписанияТипов()

 

2. Процедура "ДобавитьПоля". Благодаря наличию первой функции, код этой процедуры не меняется при использовании COM-соединения. Что довольно удобно.

Процедура ДобавитьПоля(ТаблицаСправочника)

    ТаблицаСправочника.Колонки.Добавить("КолонкаПодразделение", СтруктураОписанияТипов.ОписаниеТиповПодразделения);
    ТаблицаСправочника.Колонки.Добавить("КолонкаСотрудник", СтруктураОписанияТипов.ОписаниеТиповСотрудники);
    ТаблицаСправочника.Колонки.Добавить("КолонкаФизическоеЛицо", СтруктураОписанияТипов.ОписаниеТиповФизическиеЛица);
    ТаблицаСправочника.Колонки.Добавить("КолонкаДолжность", СтруктураОписанияТипов.ОписаниеТиповДолжности);

    ТаблицаСправочника.Колонки.Добавить("КолонкаСтрока9", СтруктураОписанияТипов.ОписаниеТиповСтроки9);
    ТаблицаСправочника.Колонки.Добавить("КолонкаСтрока150", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("КолонкаСтрока350", СтруктураОписанияТипов.ОписаниеТиповСтроки350);

    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло15_2", СтруктураОписанияТипов.ОписаниеТиповЧисла15_2);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло15_3", СтруктураОписанияТипов.ОписаниеТиповЧисла15_3);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло3_0", СтруктураОписанияТипов.ОписаниеТиповЧисла3_0);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло6_0", СтруктураОписанияТипов.ОписаниеТиповЧисла6_0);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло10_0", СтруктураОписанияТипов.ОписаниеТиповЧисла10_0);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло10_2", СтруктураОписанияТипов.ОписаниеТиповЧисла10_2);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло6_2", СтруктураОписанияТипов.ОписаниеТиповЧисла6_2);
    ТаблицаСправочника.Колонки.Добавить("КолонкаЧисло8_2", СтруктураОписанияТипов.ОписаниеТиповЧисла8_2);

    ТаблицаСправочника.Колонки.Добавить("КолонкаБулево", СтруктураОписанияТипов.ОписаниеТиповБулево);
    ТаблицаСправочника.Колонки.Добавить("КолонкаДата", СтруктураОписанияТипов.ОписаниеТиповДаты);

КонецПроцедуры // ДобавитьПоля()

Благодаря этим двум методам, у меня "сборка" новой таблицы занимает буквально минуту. Копирую поля нужных мне типов и из запроса копирую в ДобавитьПоля сами названия полей. Пример созданной реальной таблицы для выгрузки документа "Прием на работу в организацию":

Процедура ДобавитьПоляПриемыНаРаботуРаботающие(ТаблицаСправочника)

    ТаблицаСправочника.Колонки.Добавить("Организация", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("Сотрудник", СтруктураОписанияТипов.ОписаниеТиповСотрудники);
    ТаблицаСправочника.Колонки.Добавить("ВидЗанятости", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ВидДоговора", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ДатаПриема", СтруктураОписанияТипов.ОписаниеТиповДаты);
	
    ТаблицаСправочника.Колонки.Добавить("ФизическоеЛицо", СтруктураОписанияТипов.ОписаниеТиповФизическиеЛица);
    ТаблицаСправочника.Колонки.Добавить("ФИО", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ДатаРождения", СтруктураОписанияТипов.ОписаниеТиповДаты);
	
    ТаблицаСправочника.Колонки.Добавить("НомерСтроки", СтруктураОписанияТипов.ОписаниеТиповЧисла6_0);
    ТаблицаСправочника.Колонки.Добавить("ЗанимаемыхСтавок", СтруктураОписанияТипов.ОписаниеТиповЧисла6_2);
    ТаблицаСправочника.Колонки.Добавить("ИспытательныйСрок", СтруктураОписанияТипов.ОписаниеТиповЧисла6_0);

    ТаблицаСправочника.Колонки.Добавить("ДатаУвольнения", СтруктураОписанияТипов.ОписаниеТиповДаты);
    ТаблицаСправочника.Колонки.Добавить("ГрафикРаботы", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("УсловияПриемаНаРаботу", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("Сторно", СтруктураОписанияТипов.ОписаниеТиповБулево);
    ТаблицаСправочника.Колонки.Добавить("ТрудоваяФункция", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ОтразитьВТрудовойКнижке", СтруктураОписанияТипов.ОписаниеТиповБулево);

    ТаблицаСправочника.Колонки.Добавить("НомерПриказа", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ДатаПриказа", СтруктураОписанияТипов.ОписаниеТиповДаты);
    ТаблицаСправочника.Колонки.Добавить("ПодразделениеНаименование", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ПодразделениеКод", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("Аванс", СтруктураОписанияТипов.ОписаниеТиповЧисла15_2);
    ТаблицаСправочника.Колонки.Добавить("СпособРасчетаАванса", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ДолжностьНаименование", СтруктураОписанияТипов.ОписаниеТиповСтроки150);
    ТаблицаСправочника.Колонки.Добавить("ДолжностьКод", СтруктураОписанияТипов.ОписаниеТиповСтроки150);

КонецПроцедуры // ДобавитьПоляПриемыНаРаботуРаботающие()

 

Этап 3. Чтение данных. Адаптация кода под работу через COM-соединение.

 

По большей части, адаптировать нужно моменты, описанные в первых двух этапах. НО! Есть и сюрпризы… Вот какие:

  • Не работает получение данных из коллекции по индексу через квадратные скобки.

Стоит отметить, что получение значения реквизита через квадратные скобки срабатывает! Примеры с комментариями:

//Получение значение реквизита по имени работает
Для Сч = 1 По ТекСтрока.КоличествоУровней Цикл
    ТекПодразделение = ТекСтрока["Уровень" + Сч + "Ссылка"];

КонецЦикла;

//По этой же теме
СтруктураТаблицКадровогоУчета[ИмяТаблицы]

//Получение данных из коллекции. 
//Как не работает:
//#1
ДатаРегистрации = ДатыРегистрации[СтрокаФизЛицо.ФизическоеЛицо];

//Как работает:
//Выручает метод Получить()
ДатаРегистрации = ДатыРегистрации.Получить(Выборка.ФизическоеЛицо);

//#2
//Работа с результатом запроса. Мы привыкли так писать:
ВыборкаВычетыНаДетей = Результат[КолРез - 1].Выбрать();

//А работает только вот так:
ВыборкаВычетыНаДетей = Результат.Получить(КолРез - 1).Выбрать();

//#3
//К первой строке коллекции привыкли обращаться вот так:
НоваяСтрока.КодВычетаЛичный = СтрокиВычетыФизлиц[0].КодВычетаЛичный;


//Правильно через метод Получить()
НоваяСтрока.КодВычетаЛичный = СтрокиВычетыФизлиц.Получить(0).КодВычетаЛичный;

 

  • Не все методы глобального контекста срабатывают. Примеры кода с комментариями:

 

//1. Проверка заполнения ссылочных типов. При этом, если значение примитивного типа и оно заполнено, 
//в результате будет Истина. Но лучше и для них добавлять ПодключениеКБазе

//Не работает
ЗначениеЗаполнено(Выборка.ГоловнаяОрганизация)

//Так правильно:
ПодключениеКБазе.ЗначениеЗаполнено(Выборка.ГоловнаяОрганизация)

//Почему-то работает вот такой код (Выборка - это COM-объект!):
Если ПодключениеКБазе.ЗначениеЗаполнено(Выборка.ДополнительныйОтпуск) И ЗначениеЗаполнено(Выборка.ДатаНачалаДоп) И ЗначениеЗаполнено(Выборка.ДатаОкончанияДоп) И Выборка.ДатаНачалаДоп <= Выборка.ДатаОкончанияДоп Тогда
    //...
КонецЕсли;

//2. Работа с примитивными типами в глобальном контексте сохраняется. Например, такой код сработает:
ИФНС = Лев(Выборка.КПП, 4);

//3. Заполнить значения свойств. 
//Несмотря на то, что обе переменные содержат только реквизиты примитивных типов данных - не заполняет ничего!

//Не работает
ЗаполнитьЗначенияСвойств(НоваяСтрокаКонтактнойИнформации, ЗначенияПолей);

//Работает
ПодключениеКБазе.ЗаполнитьЗначенияСвойств(НоваяСтрокаКонтактнойИнформации, ЗначенияПолей);

//Почему-то работает вот такое (Выборка - это COM-объект!):
ЗаполнитьЗначенияСвойств(СтрокаКонтактнойИнформации, Выборка, "ФИО, ДатаРождения");

4. Не работают привычные нам ТипЗнч() и Тип(). Ведь все привыкли проверять тип переменных вот так
//Не работает
Если ТипЗнч(ВидКонтактнойИнформации) = Тип("СправочникСсылка.ВидыКонтактнойИнформации") Тогда
    //...
КонецЕсли;

//Работает только так
Если ПодключениеКБазе.XMLТипЗнч(ВидКонтактнойИнформации).TypeName = "CatalogRef.ВидыКонтактнойИнформации" Тогда
    //...
КонецЕсли;

 

  • Обращение к предопределённым элементам также с особенностями. Не работает сравнение ссылок.

Просто получение предопределенного элемента, и добавление его в массив или список значений отлично работает:

//Предопределенные
СписокЗначений = ПодключениеКБазе.NewObject("СписокЗначений"); 
	
СписокЗначений.Добавить(ПодключениеКБазе.Справочники.ПоказателиСхемМотивации.ТарифнаяСтавкаДневная);
СписокЗначений.Добавить(ПодключениеКБазе.Справочники.ПоказателиСхемМотивации.ТарифнаяСтавкаМесячная);
СписокЗначений.Добавить(ПодключениеКБазе.Справочники.ПоказателиСхемМотивации.ТарифнаяСтавкаЧасовая); 

//Обращение к другим метаданным также работает
РеглВалюта = ПодключениеКБазе.Константы.ВалютаРегламентированногоУчета.Получить();
СправочникМенеджер = ПодключениеКБазе.Справочники.ВидыКонтактнойИнформации;

//Сравнение значений ссылочных типов, в т.ч. с предопределенными не работает!
//#Для перечислений 
//Не работающий вариант
Если ТипКонтактнойИнформации = Перечисления.ТипыКонтактнойИнформации.Адрес Тогда
    //...
КонецЕсли;

//Работающий вариант
//Т.к. сравнение идёт в рамках одной базы, можем сравнить индекс перечисления
ТипАдрес = ПодключениеКБазе.Перечисления.ТипыКонтактнойИнформации.Адрес;
Если ПодключениеКБазе.Перечисления.ТипыКонтактнойИнформации.Индекс(ТипКонтактнойИнформации) = ПодключениеКБазе.Перечисления.ТипыКонтактнойИнформации.Индекс(ТипАдрес) Тогда
    //...
КонецЕсли;

//#Для справочников
//Не работающий вариант
Если ВидКонтактнойИнформации = Справочники.ВидыКонтактнойИнформации.ФактАдресФизЛица Тогда
    //...
КонецЕсли;

//Работающий вариант
Если ПодключениеКБазе.XMLСтрока(ВидКонтактнойИнформации) = ПодключениеКБазе.XMLString(ПодключениеКБазе.Справочники.ВидыКонтактнойИнформации.ФактАдресФизЛица) Тогда
    //...
КонецЕсли;

 

  • Вызов процедур и функций из общих модулей. Пример с комментариями:
//Данный код написан на стороне базы приёмника, т.е. через COM. 
//Случайно обнаружил его в процессе написания статьи. 
//Оказалось что в УПП и ЗУП 3.1 есть и общий модуль и функция с тем же названием. 
//Т.к. параметр строковый, всё сработало
//Разложим строку в массив уровней
МассивУровней = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ПолноеНаименование, "/");
Для Сч = 0 По Уровень Цикл
    НомерРеквизита = Сч + 1;
			
    //Запишем каждый элемент в свой уровень
    НовСтрокаПодразделения["Уровень" + НомерРеквизита] = МассивУровней[Сч];
КонецЦикла; 

//Правильно вызывать так
МассивУровней = ПодключениеКБазе.СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ПолноеНаименование, "/");

При вызове методов из общих модулей следует помнить, что работаем через "Внешнее соединение". В настройках общего модуля должен стоять одноименный флажок.

В данном примере переменная "МассивУровней" это обычный "Массив", а не "COM-объект", поэтому квадратные скобки сработали. 

Этот пример не стал менять на работу с COM-объектом, т.к. не имеет практического смысла. Результат функции правильный.

 

  • Во многих случаях русскоязычный вариант не работает. Пока не попробуешь, не узнаешь в каких случаях нужен англоязычный вариант. Приведу примеры из моего опыта:
//ВыгрузитьКолонку() не сработало
СписокСотрудников = ТаблицаДанныхТабеля.UnloadColumn("Сотрудник");

//Процедура для выборки Сбросить()
ВыборкаПоПодразделениямУТ.Reset();

//Не сработало ТипЗнч()
ТипЗначенияПеречисления = ПодключениеКБазеЗУП.FromXMLType(ПодключениеКБазеЗУП.XMLTypeOf(ПодключениеКБазеЗУП.Перечисления.СостоянияСотрудника.ПустаяСсылка()));

 

Этап 4. Преобразование таблиц из COM-объекта в тип "ТаблицаЗначений".

 

Опишу принцип преобразования таблиц, который использую. Он состоит из 4-х шагов:

  • Создаём новую структуру без использования COM-объекта. В этой структуре создаём те же самые таблицы, с теми же названиями.
  • В цикле создаём таблицы значений через конструктор "Новый".
  • Для текущей таблицы получаем соответствие колонок. Этот этап не обязательный. Если у Вас в источнике и приёмнике поля называются одинаково, то его пропускаем. Название колонок копируем из COM-объекта.
  • Для каждой колонки теперь требуется определить какого она типа. В интернете на эту тему ничего не нашёл! Код написан методом проб и ошибок. Идея в следующем:

— У описания типов есть массив определенных при создании колонки типов. Т.к. в моём случае тип всегда один, могу однозначно его получить.

— Выше заранее создавал структуры с теми типами значений, которые использую при создании таблиц.

— Для определения типа значения использую простой поиск в массиве. Во всём этом было крайне тяжело сопоставить типы значений, т.к. он везде COM-объект.

— Для более точного определения типа использую квалификаторы строки и числа.

— Новый тип колонки через условия также получаю из заранее подготовленной структуры.

  • Создаю колонку с новым типом и полученным через соответствие наименованием. Таким образом, не нужно дублировать много процедур, которые использовались для создания таблиц через COM.

 

Код основной процедуры по преобразованию таблиц:

// Преобразует таблицы кадрового учета из COM-объекта в тип ТаблицаЗначений
Процедура ПреобразоватьТаблицыКадровогоУчета() Экспорт
	
    //1. Создаем новую структуру
    СтруктураТаблицКадровогоУчетаБезCOM = СоздатьСтруктуруТаблицБезCOM();
	
    //2. Создаем в цикле таблицы
    Для Каждого ТекТаблица Из СтруктураТаблицКадровогоУчета Цикл
		
        ТаблицаБезCOM = Новый ТаблицаЗначений;
		
        //3. Получаем соответствие колонок
        СоответствиеКолонок = ПолучениеСоответствиеКолонокПоТаблице(ТекТаблица.Ключ);
		
        //4. В цикле по колонкам ...
        Для Каждого ТекКолонка Из ТекТаблица.Значение.Колонки Цикл
			
            НовыйТипКолонки = Неопределено;
			
            //-- Определяем тип колонки. Длину строки и числа определяем по квалификаторам
            ТипКолонки = ТекКолонка.ValueType.Types().Получить(0);
            Если Не СтруктураОписанияТипов.ОписаниеТиповБулево.Types().Find(ТипКолонки) = Неопределено Тогда
                //Тип Булево
                НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповБулево;
				
            ИначеЕсли Не СтруктураОписанияТипов.ОписаниеТиповДаты.Types().Find(ТипКолонки) = Неопределено Тогда
                //Тип Дата
                НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповДаты;

            ИначеЕсли Не СтруктураОписанияТипов.ОписаниеТиповСтроки150.Types().Find(ТипКолонки) = Неопределено Тогда
                //Тип Строка
                Если ТекКолонка.ValueType.StringQualifiers.Length = 150 Тогда
                    НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповСтроки150;
                ИначеЕсли ТекКолонка.ValueType.StringQualifiers.Length = 350 Тогда
                    НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповСтроки350;
                ИначеЕсли ТекКолонка.ValueType.StringQualifiers.Length = 9 Тогда
                    НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповСтроки9;
                КонецЕсли;
				
                //Тип строки по умолчанию
                Если НовыйТипКолонки = Неопределено Тогда
                    НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповСтроки150;
                КонецЕсли;
				
            ИначеЕсли Не СтруктураОписанияТипов.ОписаниеТиповЧисла6_0.Types().Find(ТипКолонки) = Неопределено Тогда
                //Тип Число
                Если ТекКолонка.ValueType.NumberQualifiers.Digits = 15 Тогда
                    Если ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 2 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла15_2;
                    ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 3 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла15_3;
                    КонецЕсли;
                ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.Digits = 3 Тогда
                    Если ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 0 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла3_0;
                    КонецЕсли;
                ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.Digits = 6 Тогда
                    Если ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 0 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла6_0;
                    ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 2 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла6_2;
                    КонецЕсли;
                ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.Digits = 10 Тогда
                    Если ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 0 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла10_0;
                    ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 2 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла10_2;
                    КонецЕсли;
                ИначеЕсли ТекКолонка.ValueType.NumberQualifiers.Digits = 8 Тогда
                    Если ТекКолонка.ValueType.NumberQualifiers.FractionDigits = 2 Тогда
                        НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла8_2;
                    КонецЕсли;
                КонецЕсли;
				
                //Тип числа по умолчанию
                Если НовыйТипКолонки = Неопределено Тогда
                    НовыйТипКолонки = СтруктураОписанияТиповБезCOM.ОписаниеТиповЧисла6_0;
                КонецЕсли;
            КонецЕсли;

            //-- Получаем соответствие колонки
            НовоеНаименованиеКолонки = СоответствиеКолонок.Получить(ТекКолонка.Name);  
			
            Если Не ЗначениеЗаполнено(НовоеНаименованиеКолонки) Тогда
                Продолжить;
            КонецЕсли;
			
            //-- Создаем новую колонку
            ТаблицаБезCOM.Колонки.Добавить(НовоеНаименованиеКолонки, НовыйТипКолонки);
        КонецЦикла; 
    КонецЦикла;

КонецПроцедуры // ПреобразоватьТаблицыКадровогоУчета()

 

Код вспомогательных функций:

Функция СоздатьСтруктуруТаблицБезCOM()
	
    СтруктураТаблиц = Новый Структура;
	
    Для Каждого ТекТаблица Из СтруктураТаблицКадровогоУчета Цикл
	
        СтруктураТаблиц.Вставить(ТекТаблица.Ключ);

    КонецЦикла;
	
    Возврат СтруктураТаблиц;
	
КонецФункции // СоздатьСтруктуруТаблицБезCOM()

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

КонецФункции // ПолучениеСоответствиеКолонокПоТаблице()

Функция ПолучитьСоответствиеКолонок()

    СоответствиеКолонок = Новый Соответствие;
	
    СоответствиеКолонок.Вставить("ИсходноеПоле1",  "НовоеПоле1");
    СоответствиеКолонок.Вставить("ИсходноеПоле2",  "НовоеПоле2");
    СоответствиеКолонок.Вставить("ИсходноеПоле3",  "НовоеПоле3");
    СоответствиеКолонок.Вставить("ИсходноеПоле4",  "НовоеПоле4");
    СоответствиеКолонок.Вставить("ИсходноеПоле5",  "НовоеПоле5");
    СоответствиеКолонок.Вставить("ИсходноеПоле6",  "НовоеПоле6");
    СоответствиеКолонок.Вставить("ИсходноеПоле7",  "НовоеПоле7");
    СоответствиеКолонок.Вставить("ИсходноеПоле8",  "НовоеПоле8");
    СоответствиеКолонок.Вставить("ИсходноеПоле9",  "НовоеПоле9");
    СоответствиеКолонок.Вставить("ИсходноеПоле10",  "НовоеПоле10");
	
    Возврат СоответствиеКолонок;
	
КонецФункции // ПолучитьСоответствиеКолонок()

 

Теперь довольно важный момент! При выборе этой технологии нужно учесть, что это не самая быстрая технология обмена данными.

Однако! Эта технология используется до сих пор во всех тиражных решениях вендора.

Я лично её выбираю за стабильность. В моём опыте не было непонятных проблем/ошибок/сбоев. 

Также ориентируюсь на то, что далеко не в каждой организации есть Web-сервер.

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

 

В публикациях ниже также есть примеры универсального кода:

Помощник заполнения графиков при вахтовом методе работы

Отладка временных таблиц и типа ТаблицаЗначений

Чтение данных из Excel. Шаблон кода

 

Предыдущие публикации:

Как читать чужой код? Часть 4. Программный интерфейс. Исправление чужих доработок

Я — ЗУПер! Часть 1. Компетенции сотрудников. (продолжение в апреле)

Ни в ЗУП ногой!? А мне нравится! Часть 1. Главные сложности решения, что отталкивает 

has been added to your cart:
Оформление заказа