При межсетевой интеграции и выгрузке данных между базами 1С через механизм COM-соединения разработчики часто сталкиваются с ситуацией, когда синтаксических ошибок не возникает, соединение устанавливается успешно, однако запрос к виртуальной таблице остатков регистра бухгалтерии возвращает совершенно пустую таблицу. Разберем типичные ошибки при передаче параметров в COM-запрос, проанализируем поведение функции получения счета и выясним, как правильно работать с составными типами субконто.
Рассмотрим типовой сценарий, в котором формируется COM-запрос к удаленной базе для выборки остатков товаров по регистру бухгалтерии Хозрасчетный:
СписокНоменклатуры = V8.NewObject("СписокЗначений");
Для каждого ОстатокНоменклатуры из ОстаткиНоменклатуры Цикл
НайденнаяНоменклатура = V8.Справочники.Номенклатура.НайтиПоКоду(ОстатокНоменклатуры.Код);
Если НЕ НайденнаяНоменклатура.Пустая() Тогда
СписокНоменклатуры.Добавить(НайденнаяНоменклатура.Ref);
КонецЕсли;
КонецЦикла;
олеЗапрос = V8.NewObject("Query");
олеЗапрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.Субконто1.Код КАК Код,
| ХозрасчетныйОстатки.Субконто1.Ссылка КАК Товар,
| ХозрасчетныйОстатки.КоличествоОстатокДт КАК Остаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(
| &Дата,
| Счет = &Счет,
| &ВидыСубконто,
| Организация = &Организация
| И Субконто1 В (&Товары)) КАК ХозрасчетныйОстатки
|ГДЕ
| ХозрасчетныйОстатки.КоличествоОстатокДт > 0";
олеЗапрос.УстановитьПараметр("Счет", V8.ПланыСчетов.Хозрасчетный.НайтиПоНаименованию("ТоварыНаСкладах"));
олеЗапрос.УстановитьПараметр("ВидыСубконто", V8.ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.НайтиПоНаименованию("Номенклатура"));
олеЗапрос.УстановитьПараметр("Организация", V8.Справочники.Организации.НайтиПоРеквизиту("ИНН", ОрганизацияКонтрагент.ИНН));
олеЗапрос.УстановитьПараметр("Товары", СписокНоменклатуры);
олеЗапрос.УстановитьПараметр("Дата", ДатаКон);
олеОстаткиНоменклатуры = олеЗапрос.Выполнить().Выгрузить();
Несмотря на то, что в удаленной базе заведомо присутствуют дебетовые остатки, результат выгрузки оказывается пустым. Разберем по шагам, где кроются ошибки.
НайтиПоНаименованию("ТоварыНаСкладах") ищет точное совпадение по пользовательскому реквизиту Наименование. В типовых конфигурациях строка "ТоварыНаСкладах" — это имя предопределенного счета в метаданных (свойство Имя), в то время как само наименование содержит пробелы: "Товары на складах". В итоге метод возвращает Неопределено или пустую ссылку, из-за чего виртуальная таблица отсекает все записи.НайтиПоКоду в цикле порождает сотни межпроцессных обращений, что замедляет выполнение и повышает риск таймаутов соединения.Субконто1 в регистре имеет составной тип, что создает трудности при чтении реквизитов через точку.Для гарантированной фильтрации по нужному счету рассмотрим три надежных варианта:
Вариант 1. Поиск по коду счета
Если код счета фиксирован (например, 41.01), можно использовать метод поиска по коду:
СчетТовары = V8.ПланыСчетов.Хозрасчетный.НайтиПоКоду("41.01");
олеЗапрос.УстановитьПараметр("Счет", СчетТовары);
Вариант 2. Обращение к предопределенному счету через контекст COM
Для исключения ошибок со сменой кодов или наименований в пользовательском режиме обратимся напрямую к предопределенному значению базы через глобальный контекст или менеджер плана счетов:
// Обращение через свойство менеджера
СчетТовары = V8.ПланыСчетов.Хозрасчетный.ТоварыНаСкладах;
олеЗапрос.УстановитьПараметр("Счет", СчетТовары);
// Либо получение через функцию глобального контекста удаленной базы
СчетТовары = V8.ПредопределенноеЗначение("ПланСчетов.Хозрасчетный.ТоварыНаСкладах");
олеЗапрос.УстановитьПараметр("Счет", СчетТовары);
Вариант 3. Использование конструкции ЗНАЧЕНИЕ прямо в тексте запроса
Наиболее надежный и производительный путь — вообще отказаться от передачи параметра через COM и зафиксировать счет в самом тексте запроса. Обратите внимание: имя объекта в функции ЗНАЧЕНИЕ всегда указывается в единственном числе (ПланСчетов, а не ПланыСчетов):
ТекстЗапроса =
"ВЫБРАТЬ
| ...
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(
| &Дата,
| Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ТоварыНаСкладах),
| &ВидыСубконто,
| Организация = &Организация) КАК ХозрасчетныйОстатки";
При обращении к свойству ХозрасчетныйОстатки.Субконто1.Код разработчики часто сталкиваются с ситуацией, когда возвращенный код номенклатуры содержит в конце множество пробелов, а его длина составляет 25–30 символов вместо ожидаемых 9 или 12.
Выясним причину: измерение Субконто1 в регистре бухгалтерии имеет составной тип данных (ссылки на различные справочники, входящие в план видов характеристик). Когда платформа выполняет неявное соединение через разыменование точки, СУБД формирует строковое поле с максимальной длиной кода среди всех возможных таблиц субконто, дополняя строку пробелами до максимального размера фиксированной строки (тип CHAR/NCHAR).
Чтобы получить точное значение кода без лишней длины, применим явное приведение типа с помощью функции ВЫРАЗИТЬ прямо в запросе:
ВЫРАЗИТЬ(ХозрасчетныйОстатки.Субконто1 КАК Справочник.Номенклатура).Код КАК Код
При таком подходе платформа обращается исключительно к таблице справочника Номенклатура, и результирующее поле получает строго определенную длину реквизита без лишних концевых пробелов.
Чтобы не выполнять медленный построчный поиск в цикле через COM-соединение, оптимизируем код: передадим в COM-базу простой массив строковых кодов и наложим условие прямо внутри виртуальной таблицы остатков.
Посмотрим на итоговый оптимизированный пример:
// Формируем простой массив строк в локальной базе
МассивКодов = Новый Массив;
Для каждого Стр Из ОстаткиНоменклатуры Цикл
МассивКодов.Добавить(Стр.Код);
КонецЦикла;
олеЗапрос = V8.NewObject("Query");
олеЗапрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(ХозрасчетныйОстатки.Субконто1 КАК Справочник.Номенклатура).Код КАК Код,
| ВЫРАЗИТЬ(ХозрасчетныйОстатки.Субконто1 КАК Справочник.Номенклатура) КАК Товар,
| ХозрасчетныйОстатки.КоличествоОстатокДт КАК Остаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(
| &Дата,
| Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ТоварыНаСкладах),
| ЗНАЧЕНИЕ(ПланВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура),
| Организация = &Организация
| И ВЫРАЗИТЬ(Субконто1 КАК Справочник.Номенклатура).Код В (&МассивКодов)) КАК ХозрасчетныйОстатки
|ГДЕ
| ХозрасчетныйОстатки.КоличествоОстатокДт > 0";
// Примитивные типы (массивы, строки, даты) передаются через COM без создания тяжелых прокси-объектов
олеЗапрос.УстановитьПараметр("МассивКодов", МассивКодов);
олеЗапрос.УстановитьПараметр("Дата", ДатаКон);
олеЗапрос.УстановитьПараметр("Организация", V8.Справочники.Организации.НайтиПоРеквизиту("ИНН", ОрганизацияКонтрагент.ИНН));
олеОстаткиНоменклатуры = олеЗапрос.Выполнить().Выгрузить();
В этом решении счет и вид субконто зафиксированы через конструкцию ЗНАЧЕНИЕ, что полностью исключает ошибки поиска объектов удаленной базы по строковым наименованиям, а фильтрация по массиву кодов выполняется на стороне сервера удаленной базы за один вызов.