Как правильно получить и обработать остатки из 1С 7.7 в 1С 8.3 через COM-соединение

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

При интеграции устаревших конфигураций на платформе 1С 7.7 (например, "Торговля и Склад 7.7") с современными базами на платформе 1С 8.3 разработчики часто используют COM-соединение (OLE). Одним из наиболее узких мест при таком подходе является построение и выполнение запросов на стороне 1С 7.7 из кода 1С 8.3. Синтаксис "черных запросов" 1С 7.7 существенно отличается от языка запросов 1С 8.x, что приводит к неочевидным ошибкам, когда запрос возвращает пустой результат или нулевые остатки, хотя в самой семерке отчеты показывают корректные данные.

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

---

Почему стандартный запрос через COM возвращает нулевые остатки?

Рассмотрим первоначальный код запроса, с которым столкнулся автор темы, и проанализируем основные ошибки, из-за которых функция КонОст() возвращала «0»:


// Фрагмент проблемного запроса
|ФирмаКод = Регистр.ОстаткиТМЦ.Фирма.Код;
|НоменклатураР = Регистр.ОстаткиТМЦ.Номенклатура.Родитель;
|...
|Условие (ФирмаКод = '00017');
|Условие (НоменклатураР = 'р2649');

Выясним основные причины неработоспособности такого запроса:

  1. Сравнение ссылочного типа со строкой. Переменная запроса НоменклатураР принимает значение типа «СправочникСсылка.Номенклатура» (Родитель). В условии происходит попытка сравнить эту ссылку со строковым литералом 'р2649'. В 1С 7.7 такое сравнение всегда будет ложным, из-за чего выборка окажется пустой.
  2. Неверный формат кавычек (апострофы). Использование одиночных апострофов '00017' в условиях запроса 1С 7.7 часто интерпретируется системой как дата или некорректный литерал, особенно при передаче через COM. В запросах 1С 7.7 строковые константы необходимо оборачивать в двойные кавычки. В коде 1С 8.3 это выглядит как удвоенные кавычки внутри строки.
  3. Отсутствие периода запроса. При выполнении запроса через COM-соединение точка актуальности (ТА) может быть не определена или инициализирована некорректно. Если не указать период явно, система попытается рассчитать итоги на неопределенный момент времени, что вернет нулевые значения остатков.

---

Решение 1: Исправление синтаксиса условий и настройка периода

Для того чтобы запрос 1С 7.7 успешно отработал, перепишем условия. Вместо сравнения объектов со строками будем сравнивать их строковые коды, а также заменим апострофы на двойные кавычки. Обязательно добавим явное указание периода.

Разберем пример формирования текста запроса на стороне 1С 8.3:


// Формируем дату в формате 1С 7.7 (ДД.ММ.ГГГГ или строка вида 'ДД.ММ.ГГГГ')
ДатаЗапросаТекст = "'" + Формат(Объект.ДатаОстатков, "ДФ=dd.MM.yyyy") + "'";

ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с " + ДатаЗапросаТекст + " по " + ДатаЗапросаТекст + ";
|Фирма = Регистр.ОстаткиТМЦ.Фирма;
|ФирмаКод = Регистр.ОстаткиТМЦ.Фирма.Код;
|Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
|НоменклатураРодительКод = Регистр.ОстаткиТМЦ.Номенклатура.Родитель.Код;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|СкладКод = Регистр.ОстаткиТМЦ.Склад.Код;
|Количество = Регистр.ОстаткиТМЦ.Количество;
|Функция КоличествоКонОст = КонОст(Количество);
|Группировка Номенклатура без групп;
|Группировка Склад без групп;
|Условие (ФирмаКод = ""00017"");
|Условие (НоменклатураРодительКод = ""р2649"");
|Условие (СкладКод = ""00060"");
|//}}ЗАПРОС";

Важные детали:

---

Решение 2: Передача ссылочных объектов через временные файлы

Что делать, если отбор по коду нам не подходит (например, коды неуникальны или группа номенклатуры выбирается динамически на стороне 1С 8.3 как объект)? Передать непосредственно ссылку 1С 8.3 в условие запроса 1С 7.7 невозможно.

Рассмотрим элегантное решение, предложенное в сообщении 65: передача ссылки через механизм сериализации ЗначениеИзФайла.

Алгоритм действий выглядит следующим образом:

  1. Создаем OLE-объект справочника номенклатуры на стороне 7.7.
  2. Находим нужную группу (родителя) по коду или наименованию в базе 7.7.
  3. Сохраняем ссылку на этот объект во временный файл на диске с помощью метода ЗначениеВФайл.
  4. В тексте запроса используем функцию ЗначениеИзФайла, которая считает объект прямо в контекст выполнения запроса.

Посмотрим на программную реализацию этого метода:


// Инициализируем объекты OLE базы 7.7
СпрНоменклатура77 = База77.CreateObject("Справочник.Номенклатура");

// Ищем целевую группу номенклатуры по коду "р2649"
Если СпрНоменклатура77.НайтиПоКоду("р2649") = 0 Тогда
    ВызватьИсключение "Группа номенклатуры не найдена в базе 1С 7.7!";
КонецЕсли;

// Получаем ссылку на текущий найденный элемент
ГруппаОбъект77 = СпрНоменклатура77.ТекущийЭлемент();

// Формируем путь к временному файлу
ИмяФайлаПередачи = База77.КаталогВременныхФайлов() + "v7_group_filter.val";

// Сохраняем объект во временный файл (третий параметр "1" обязателен для сохранения ссылочных данных)
База77.ЗначениеВФайл(ИмяФайлаПередачи, ГруппаОбъект77, 1);

// Строим запрос с использованием сохраненного файла в условии
ДатаЗапросаТекст = "'" + Формат(Объект.ДатаОстатков, "ДФ=dd.MM.yyyy") + "'";

ТекстЗапроса = 
"Период с " + ДатаЗапросаТекст + " по " + ДатаЗапросаТекст + ";
|Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
|НоменклатураРодитель = Регистр.ОстаткиТМЦ.Номенклатура.Родитель;
|СкладКод = Регистр.ОстаткиТМЦ.Склад.Код;
|Количество = Регистр.ОстаткиТМЦ.Количество;
|Функция КоличествоКонОст = КонОст(Количество);
|Группировка Номенклатура без групп;
|Условие (СкладКод = ""00060"");
|Условие (НоменклатураРодитель = ЗначениеИзФайла(""" + ИмяФайлаПередачи + """));";

Запрос77 = База77.CreateObject("Запрос");
Если Запрос77.Выполнить(ТекстЗапроса) = 0 Тогда
    Предупреждение("Не удалось выполнить запрос в базе 1С 7.7");
    Возврат;
КонецЕсли;

---

Решение 3: Оптимизация производительности (Избегаем COM-roundtrips)

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


Пока Запрос.Группировка("Номенклатура") = 1 Цикл
    Пока Запрос.Группировка("Склад") = 1 Цикл
        // Обращение к свойствам объекта через COM в цикле
        Наименование = Запрос.Номенклатура.Наименование;
    КонецЦикла;
КонецЦикла;

Это классический антипаттерн интеграции. Каждое обращение к методу Группировка() или к реквизиту объекта Запрос.Номенклатура.Наименование порождает сетевой вызов (круглый вызов между процессами — roundtrip). При выборке в несколько тысяч позиций такой код будет выполняться чрезвычайно медленно (до нескольких десятков минут).

Чтобы ускорить процесс в десятки раз, применим метод выгрузки результатов во временную Таблицу Значений на стороне 1С 7.7. Обход плоской таблицы значений работает в разы быстрее, так как не требует обращения к иерархическим группировкам объекта запроса.

Проанализируем правильный алгоритм обработки результата:


// Выполняем запрос
Если Запрос77.Выполнить(ТекстЗапроса) = 1 Тогда
    
    // Создаем объект ТаблицаЗначений в контексте 1С 7.7
    ТЗ77 = База77.CreateObject("ТаблицаЗначений");
    
    // Выгружаем результаты запроса в плоскую таблицу (второй и третий параметры равны 0)
    Запрос77.Выгрузить(ТЗ77, 0, 0);
    
    // Получаем количество строк
    КоличествоСтрок = ТЗ77.КоличествоСтрок();
    
    // Обходим плоскую таблицу значений в одном цикле на стороне 1С 8.3
    Для НомерСтроки = 1 По КоличествоСтрок Цикл
        
        // Получаем ссылку на номенклатуру 7.7
        НоменклатураСсылка77 = ТЗ77.ПолучитьЗначение(НомерСтроки, "Номенклатура");
        
        // Быстро считываем реквизиты объекта
        Наименование = НоменклатураСсылка77.Наименование;
        Код77 = НоменклатураСсылка77.Код;
        Остаток = ТЗ77.ПолучитьЗначение(НомерСтроки, "КоличествоКонОст");
        
        // Заполняем таблицу значений или табличную часть в 1С 8.3
        НоваяСтрока = ТаблицаОстатков83.Добавить();
        НоваяСтрока.Номенклатура77_Наименование = Наименование;
        НоваяСтрока.Номенклатура77_Код = Код77;
        НоваяСтрока.Остаток = Остаток;
        
    КонецЦикла;
    
КонецЕсли;

Использование этого подхода позволяет сократить время переноса данных по остаткам склада с нескольких минут до нескольких секунд.

← На главную