При организации интеграции или переноса данных из базы 1С:Предприятия 7.7 в конфигурации 1С:Предприятия 8 через COM-соединение (OLE Automation) разработчики нередко сталкиваются с непредвиденным поведением платформы. Типичная ситуация: документ успешно инициализирован, перебор строк табличной части выполняется штатно, большинство реквизитов считываются напрямую через точку, однако на конкретном реквизите (часто с именем Сумма) процесс прерывается критической ошибкой «Поле объекта не обнаружено».
Разберем детально, почему возникает эта коллизия, как ее оперативно обойти и какие альтернативные подходы помогут сделать перенос данных стабильным и быстрым.
Проанализируем типичный фрагмент кода в базе-приемнике 1С 8, где возникает данная проблема:
Док_КСФ = База77.CreateObject("Документ.КорректировочныйСчетФактура");
// ... позиционирование или выборка документов ...
Док_КСФ.ВыбратьСтроки();
Пока Док_КСФ.ПолучитьСтроку() = 1 Цикл
стр = ДокР.Товары.Добавить();
стр.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(Док_КСФ.Наименование.Код).Ссылка;
стр.Количество = Док_КСФ.Количество * Док_КСФ.Коэффициент;
стр.КоличествоДоИзменения = Док_КСФ.КоличествоДоИзменения * Док_КСФ.Коэффициент;
// На следующей строке генерируется исключение:
стр.Сумма = Док_КСФ.Сумма;
стр.СтавкаНДС = ВернутьСтавкуНДС(Док_КСФ.СтавкаНДС);
КонецЦикла;
При этом визуально в конфигураторе 1С 7.7 реквизит табличной части существует, тип совпадает, а латинские буквы в имени отсутствуют. Выясним причину такого поведения OLE-сервера 1С 7.7.
Посмотрим на внутреннее устройство взаимодействия двух платформ. Когда код 1С 8 обращается к свойству OLE-объекта через точку (Док_КСФ.Сумма), вызывается низкоуровневый интерфейс IDispatch::GetIDsOfNames. На стороне OLE-сервера 1С 7.7 возникает конфликт диспетчеризации по одной из следующих причин:
ПолучитьСтроку(). В ряде случаев механизм трансляции COM-запросов дает сбой при поиске динамических атрибутов.Самый надежный и простой способ обойти сбой механизма диспетчеризации свойств — обратиться к прямому методу доступа к данным метаданных 1С 7.7. Метод ПолучитьАтрибут() обращается непосредственно к внутреннему контексту текущей строки документа, минуя неявные преобразования OLE-интерфейса.
Заменим проблемную строку обращения через точку на вызов метода с строковым параметром:
Док_КСФ.ВыбратьСтроки();
Пока Док_КСФ.ПолучитьСтроку() = 1 Цикл
стр = ДокР.Товары.Добавить();
стр.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(Док_КСФ.Наименование.Код).Ссылка;
стр.Количество = Док_КСФ.Количество * Док_КСФ.Коэффициент;
стр.КоличествоДоИзменения = Док_КСФ.КоличествоДоИзменения * Док_КСФ.Коэффициент;
// Получение значения через встроенный метод метаданных:
стр.Сумма = Док_КСФ.ПолучитьАтрибут("Сумма");
стр.СтавкаНДС = ВернутьСтавкуНДС(Док_КСФ.ПолучитьАтрибут("СтавкаНДС"));
КонецЦикла;
Для записи значений в объекты базы 7.7 (если требуется обратная выгрузка) по аналогии следует использовать симметричный метод УстановитьАтрибут("ИмяРеквизита", Значение).
Построчное чтение реквизитов документа через COM-соединение порождает огромное количество межпроцессных вызовов (RPC-маршалинг). Каждое обращение к свойству или методу отдельной строки выполняется крайне медленно. Если переносятся тысячи документов с большими табличными частями, эффективнее использовать штатную выгрузку всей табличной части в объект ТаблицаЗначений на стороне 7.7.
Рассмотрим, как реализовать этот подход:
// Создаем локальную для 7.7 таблицу значений
ТЗ_Семерка = База77.CreateObject("ТаблицаЗначений");
// Выгружаем табличную часть позиционированного документа
Док_КСФ.ВыгрузитьТабличнуюЧасть(ТЗ_Семерка);
// Обходим строки полученной таблицы значений
ТЗ_Семерка.ВыбратьСтроки();
Пока ТЗ_Семерка.ПолучитьСтроку() = 1 Цикл
стр = ДокР.Товары.Добавить();
стр.Количество = ТЗ_Семерка.ПолучитьЗначение(ТЗ_Семерка.НомерСтроки, "Количество");
стр.Сумма = ТЗ_Семерка.ПолучитьЗначение(ТЗ_Семерка.НомерСтроки, "Сумма");
КонецЦикла;
Метод ВыгрузитьТабличнуюЧасть() переносит данные напрямую из физической структуры документа, полностью исключая возможные проблемы с позиционированием курсора и коллизиями свойств OLE-объекта.
Если требуется прочитать сложные реквизиты или предварительно обработать выборку данных, можно передать исполнение кода целиком во внутренний контекст 1С 7.7 с помощью метода EvalExpr(). Это позволяет проверить наличие реквизита функцией глЕстьРеквизитТабличнойЧасти или подготовить сериализованную структуру данных.
Посмотрим на пример вызова:
// Проверка наличия реквизита табличной части стандартными механизмами конфигурации 7.7:
ЕстьРеквизит = База77.EvalExpr("глЕстьРеквизитТабличнойЧасти(""Сумма"", ""КорректировочныйСчетФактура"")");
Если ЕстьРеквизит = 1 Тогда
стр.Сумма = Док_КСФ.ПолучитьАтрибут("Сумма");
КонецЕсли;
При разработке интеграций с «семеркой» необходимо помнить о системных особенностях платформы:
База77.CreateObject() или инициализация V77.Application завершится ошибкой, если код исполняется в 64-битном рабочем процессе сервера 1С 8. Фоновые задания и клиентские приложения 1С 8 для работы с 7.7 по COM должны запускаться в 32-разрядном режиме либо использовать суррогатный процесс Windows COM+ (dllhost.exe).Предупреждение()) или запрос подтверждения в коде 7.7 приведет к появлению невидимого модального окна и полной блокировке процесса переноса данных.