При интеграции современных конфигураций на платформе 1С:Предприятие 8.3 с историческими системами на базе 7.7 часто возникают задачи синхронизации данных. Одной из главных проблем становится сопоставление объектов. В "восьмерке" мы привыкли к понятию УникальныйИдентификатор (UUID/GUID), но в версии 7.7 архитектура хранения данных иная. В этой статье мы подробно разберем, как однозначно идентифицировать объекты 7.7 и как правильно читать значения реквизитов (например, перечислений) через COM-соединение. В качестве альтернативы прямому программному обмену можно использовать готовые правила конвертации данных для переноса из 1С 7.7 в 8.3.
В платформе 1С 8.3 у любой ссылки есть свойство УникальныйИдентификатор(), которое возвращает GUID. В 1С 7.7 такого типа данных нет. Однако, для синхронизации нам необходим некий ключ, который не меняется при изменении кода или наименования элемента.
Если мы обратимся к структуре базы данных 7.7, то увидим, что уникальность обеспечивается внутренним ID (в DBF или SQL таблицах это поле часто называется ID или IDDoc/IDDef). При работе через OLE/COM-соединение напрямую получить доступ к этому полю через свойства объекта нельзя. Но существует штатный метод платформы 7.7 — ЗначениеВСтрокуВнутр.
Разберем по шагам, как использовать этот метод из кода 1С 8.3.
Предположим, мы уже установили соединение с базой 7.7 и переменная База77 хранит COM-объект V77.Application. Мы получили ссылку на справочник или документ. Чтобы получить его уникальную строку, нам нужно выполнить функцию 7.7 внутри контекста 7.7. Если при подключении 64-разрядного сервера 1С к 32-битному COM-объекту 7.7 возникают ошибки разрядности, можно использовать решение для запуска COM-объекта 1С 7.7 в виде удаленного компонента COM+.
Для этого используем метод EvalExpr (ВычислитьВыражение). Рассмотрим пример кода:
// Подключение уже выполнено, переменная База77 инициализирована
// Получаем объект справочника (например, ищем по коду для теста)
Спр77 = База77.CreateObject("Справочник.Номенклатура");
Если Спр77.НайтиПоКоду("00001") = 1 Тогда
ТекОбъект77 = Спр77.ТекущийЭлемент();
// Получаем внутреннее строковое представление
// Обратите внимание: функция ЗначениеВСтрокуВнутр выполняется на стороне 7.7
ВнутрСтр = База77.EvalExpr("ЗначениеВСтрокуВнутр(Спр77.ТекущийЭлемент())");
Сообщить("Уникальный ключ 7.7: " + ВнутрСтр);
КонецЕсли;
Результат выполнения функции ЗначениеВСтрокуВнутр будет выглядеть примерно так:
{"B","0","0","412","0","0"," 297 "}
Давайте проанализируем, что содержится в этой строке. Это сериализованное представление объекта. Самая важная часть для нас — это последний элемент в кавычках (в примере выше — 297). Это и есть внутренний ID объекта в 36-ричной системе счисления. В сочетании с типом объекта (вид справочника) этот ID является уникальным.
Важно: При переносе данных вы можете использовать всю строку целиком как ключ поиска (GUID аналог), либо программно вырезать из нее последнюю часть, если вам нужен более компактный идентификатор.
Вторая распространенная проблема, с которой сталкиваются разработчики при переносе данных — получение значений реквизитов, имеющих тип Перечисление. Если в 8.3 мы обратимся к реквизиту справочника 7.7, который является перечислением, мы получим COMОбъект.
Рассмотрим ситуацию, когда у нас есть реквизит ХарактерДеятельности. Попытка просто присвоить его значение в строку или записать в базу 8.3 приведет к ошибке несоответствия типов.
Есть два основных способа получить понятное значение перечисления из 7.7.
Самый простой способ — получить то, что видит пользователь (синоним значения). В 7.7, как и в 8.3, есть глобальное приведение к строке.
// Спр77 - это спозиционированный объект справочника из базы 7.7
// Получаем COM-объект значения перечисления
ЗначениеПеречисленияCOM = Спр77.ХарактерДеятельности;
// Используем метод String() самого COM-соединения (База77)
// Он преобразует любой объект 7.7 в его строковое представление
НазваниеЗначения = База77.String(ЗначениеПеречисленияCOM);
Сообщить("Значение: " + НазваниеЗначения); // Например: "Оптовая торговля"
Этот метод удобен, если вам нужно просто вывести информацию или найти соответствие в 8.3 по наименованию (что не всегда надежно).
Для надежного переноса данных лучше опираться не на синонимы (которые могут менять пользователи или разработчики), а на идентификаторы метаданных. В 7.7 у объекта перечисления нет прямого свойства Имя или Идентификатор, доступного через точку. Нам придется обратиться к метаданным.
Алгоритм действий следующий:
Посмотрим, как это реализовать в коде 8.3:
// Получаем само значение реквизита
ЗначениеCOM = Спр77.ХарактерДеятельности;
// Проверяем, заполнено ли оно (в 7.7 пустые ссылки тоже объекты)
Если База77.EvalExpr("ПустоеЗначение(Спр77.ХарактерДеятельности)") = 0 Тогда
// Получаем вид перечисления (например, "ВидыТорговли")
ВидПеречисления = ЗначениеCOM.Вид();
// Получаем порядковый номер значения в списке (начинается с 1)
ПорядковыйНомер = ЗначениеCOM.ПорядковыйНомер();
// Обращаемся к метаданным 7.7 через COM
// Метаданные.Перечисление("Имя").Значение(Номер).Идентификатор
ИдентификаторЗначения = База77.Метаданные.Перечисление(ВидПеречисления).Значение(ПорядковыйНомер).Идентификатор;
Сообщить("Имя в конфигураторе: " + ИдентификаторЗначения); // Например: "Опт"
Иначе
Сообщить("Значение не заполнено");
КонецЕсли;
Используя переменную ИдентификаторЗначения, вы можете легко найти соответствующее значение в перечислении конфигурации 8.3, используя метод Перечисления.ИмяПеречисления[ИдентификаторЗначения] (через оператор [] или метод Имя() не сработает напрямую, но можно использовать поиск).
При получении данных из 7.7 всегда нужно помнить, что "пустая ссылка" в 7.7 при передаче через COM является объектом. Если вы попытаетесь прочитать свойства незаполненного реквизита (например, Спр77.Контрагент.Код), когда контрагент не выбран, вы получите ошибку OLE.
Поэтому перед чтением свойств ссылочных типов всегда проверяйте их на пустоту, используя функцию Selected() (Выбран) для справочников/документов или глобальную функцию ПустоеЗначение() через EvalExpr.
// Проверка ссылочного типа (Справочник, Документ)
ПолеКонтрагент = Спр77.Контрагент;
Если ПолеКонтрагент.Selected() = 1 Тогда
// Можно читать свойства
КодКонтрагента = ПолеКонтрагент.Code;
Иначе
// Значение пустое
КонецЕсли;
Подводя итог, выделим основные моменты:
ЗначениеВСтрокуВнутр().База77.EvalExpr("Функция(...)").База77.String(Объект).База77.Метаданные.