Попытаюсь изложить некоторые встреченные нюансы парсинга текста запроса в СКД - т.е. каким образом преобразуется в приведенных примерах текст запроса, написанный в окне Запроса закладки Наборы данных в СКД - далее просто ТЗ СКД. Для примеров используется конфигурация Управление торговлей, редакция 11.3.
Известно, что СКД создана в том числе и для минимизации действий кодировщика и проделывает за него некоторые скрытые действия, которые не всегда оказываются очевидными.
1. Поведение соединений к основной таблице
Создаю новый внешний отчет, для него создаю основную схему компоновки данных. Пишу в ней такой ТЗ СКД
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка,
ДокРеализация.Дата,
РеализацияТоваровУслугТовары.Номенклатура,
РеализацияТоваровУслугТовары.КоличествоУпаковок,
РеализацияТоваровУслугТовары.Цена,
РеализацияТоваровУслугТовары.Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
{
внутреннее соединение Документ.РеализацияТоваровУслуг ДокРеализация по ДокРеализация.Ссылка=РеализацияТоваровУслугТовары.Ссылка
}
{где
ДокРеализация.Дата = &ДатаОтчета
}
Фигурные скобки в ТЗ СКД означают для соединений и условий то, что данная часть ТЗ СКД будет включаться в результирующий текст запроса только при определенных пользовательских настройках.
Для того, чтобы увидеть результат преобразования в модуле отчета создаю следующую процедуру:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка=ложь;
НастройкиОтчета = КомпоновщикНастроек.ПолучитьНастройки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиОтчета, ДанныеРасшифровки);
сообщить(МакетКомпоновки.НаборыДанных.НаборДанных1.Запрос);
КонецПроцедуры
Вывожу параметр ДатаОтчета на форму, создаю простую настройку с одной группировкой "Номенклатура" без каких либо полей и запускаю формирование отчета - вначале с отключенной галочкой отбора по параметру. В окне сообщений получаю результирующий запрос, который будет использоваться системой для получения необходимых для отчета данных:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕССЫЛКИ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление,
РеализацияТоваровУслугТовары.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
Видно, что внутреннее соединение из ТЗ СКД отсутствует, т.к. ни в фильтре, ни в полях настройки нигде эта таблица не требуется.
Если установить фильтр по дата, то получаем результирующий запрос
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕССЫЛКИ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление,
РеализацияТоваровУслугТовары.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК ДокРеализация
ПО (ДокРеализация.Ссылка = РеализацияТоваровУслугТовары.Ссылка)
ГДЕ
ДокРеализация.Дата = &ДатаОтчета
Внутреннее соединение появилось. То же самое произойдет если не включать фильтр по дате, а попробовать в настройках вывести поле Дата:
ВЫБРАТЬ
ДокРеализация.Дата КАК Дата,
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕССЫЛКИ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление,
РеализацияТоваровУслугТовары.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК ДокРеализация
ПО (ДокРеализация.Ссылка = РеализацияТоваровУслугТовары.Ссылка)
Теперь дописываю ТЗ СКД, включая еще одно соединение - со справочником Партнеры:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка,
ДокРеализация.Дата,
РеализацияТоваровУслугТовары.Номенклатура,
РеализацияТоваровУслугТовары.КоличествоУпаковок,
РеализацияТоваровУслугТовары.Цена,
РеализацияТоваровУслугТовары.Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
{
внутреннее соединение Документ.РеализацияТоваровУслуг ДокРеализация по ДокРеализация.Ссылка=РеализацияТоваровУслугТовары.Ссылка
}
{
левое соединение Справочник.Партнеры СпрПартнеры по СпрПартнеры.Ссылка=ДокРеализация.Партнер
}
{где
ДокРеализация.Дата = &ДатаОтчета,
СпрПартнеры.Ссылка.* Как Партнер
}
Формирую с условием по партнеру, но без условия по дате и без полей-группировок по дате в запросе. В результирующем запросе получаю не совсем то, что ожидал:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
ПРЕДСТАВЛЕНИЕССЫЛКИ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление,
РеализацияТоваровУслугТовары.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары,
Справочник.Партнеры КАК СпрПартнеры
ГДЕ
СпрПартнеры.Ссылка = &П
Для избежания подобного результата нужно либо убирать фигурные скобки в первом соединении - тогда оно будет включатся в результирующий запрос всегда, либо оба соединения заключать в общие фигурные скобки - тогда оба соединения будут включены в текст запроса в случаях, когда в пользовательских настройках требуется либо получение даты либо получение партнера. Четкой рекомендации для подобных случаев дать не могу, кроме как просматривать результирующий текст запроса при отладке отчета при разных вариантах настроек.
2. Неочевидное включение фильтров
В приведенном примере требуется показать номенклатуру из документов Реализация по определенному складу и вывести дополнительно остаток по номенклатуре по всем складам суммарно. Пишу следующий ТЗ СКД:
Выбрать Номенклатура,ТоварыНаСкладахОстатки.ВНаличииОстаток
Поместить ТаблицаОстаткиНоменклатуры
из РегистрНакопления.ТоварыНаСкладах.Остатки(,) КАК ТоварыНаСкладахОстатки
;
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка,
ДокРеализация.Дата,
РеализацияТоваровУслугТовары.Номенклатура,
РеализацияТоваровУслугТовары.КоличествоУпаковок,
ТаблицаОстаткиНоменклатуры.ВНаличииОстаток Остаток,
РеализацияТоваровУслугТовары.Цена,
РеализацияТоваровУслугТовары.Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
{
внутреннее соединение Документ.РеализацияТоваровУслуг ДокРеализация по ДокРеализация.Ссылка=РеализацияТоваровУслугТовары.Ссылка
}
левое соединение ТаблицаОстаткиНоменклатуры по ТаблицаОстаткиНоменклатуры.Номенклатура=РеализацияТоваровУслугТовары.Номенклатура
{где
ДокРеализация.Дата = &ДатаОтчета,
ДокРеализация.Склад.* Как Склад
}
формирую отчет с фильтром по складу и получаю результирующий запрос в таком виде:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ВНаличииОстаток
ПОМЕСТИТЬ ТаблицаОстаткиНоменклатуры
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &П) КАК ТоварыНаСкладахОстатки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
ТаблицаОстаткиНоменклатуры.ВНаличииОстаток КАК Остаток,
ПРЕДСТАВЛЕНИЕССЫЛКИ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление,
РеализацияТоваровУслугТовары.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК ДокРеализация
ПО (ДокРеализация.Ссылка = РеализацияТоваровУслугТовары.Ссылка)
ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаОстаткиНоменклатуры КАК ТаблицаОстаткиНоменклатуры
ПО (ТаблицаОстаткиНоменклатуры.Номенклатура = РеализацияТоваровУслугТовары.Номенклатура)
ГДЕ
ДокРеализация.Склад = &П
Здесь видно, что система сама включила фильтр по складу во виртуальную таблицу остатков. Не очень ожидаемое действие. Приходится для избежания этого назначать другое имя переменной для фильтра по складу документа:
...
{где
ДокРеализация.Дата = &ДатаОтчета,
ДокРеализация.Склад.* Как СкладРеализации
}
Тогда отбор в виртуальной таблице уже отсутствует.
3. Управление формированием временных таблиц
Случается, что для организации фильтра удобно использование временных таблиц. Опишу, каким образом удобно включать/отключать формирование временной таблицы только тогда, когда она требуется по текущим настройкам пользователя. ТЗ СКД:
Выбрать Номенклатура
Поместить ТаблицаОстаткиНоменклатуры
из РегистрНакопления.ТоварыНаСкладах.Остатки(,
{Склад.* Как СкладОстатков,Номенклатура.* Как Номенклатура}
) КАК ТоварыНаСкладахОстатки
где ТоварыНаСкладахОстатки.ВНаличииОстаток>0
индексировать по Номенклатура
;
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка,
ДокРеализация.Дата,
РеализацияТоваровУслугТовары.Номенклатура,
РеализацияТоваровУслугТовары.КоличествоУпаковок,
РеализацияТоваровУслугТовары.Цена,
РеализацияТоваровУслугТовары.Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
{
внутреннее соединение Документ.РеализацияТоваровУслуг ДокРеализация по ДокРеализация.Ссылка=РеализацияТоваровУслугТовары.Ссылка
}
{где
//РеализацияТоваровУслугТовары.Номенклатура.* Как Номенклатура,
ДокРеализация.Дата = &ДатаОтчета,
&ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры и РеализацияТоваровУслугТовары.Номенклатура в (Выбрать Номенклатура из ТаблицаОстаткиНоменклатуры)
}
Модуль отчета будет выглядеть таким образом:
Процедура НайтиРекурсивно(КоллекцияЭлементов, МассивЭлементов, СпособПоиска, ЗначениеПоиска)
Для каждого ЭлементОтбора Из КоллекцияЭлементов Цикл
Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
Если СпособПоиска = 1 Тогда
Если ЭлементОтбора.ЛевоеЗначение = ЗначениеПоиска Тогда
МассивЭлементов.Добавить(ЭлементОтбора);
КонецЕсли;
ИначеЕсли СпособПоиска = 2 Тогда
Если ЭлементОтбора.Представление = ЗначениеПоиска Тогда
МассивЭлементов.Добавить(ЭлементОтбора);
КонецЕсли;
КонецЕсли;
Иначе
НайтиРекурсивно(ЭлементОтбора.Элементы, МассивЭлементов, СпособПоиска, ЗначениеПоиска);
Если СпособПоиска = 2 И ЭлементОтбора.Представление = ЗначениеПоиска Тогда
МассивЭлементов.Добавить(ЭлементОтбора);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция НайтиЭлементыИГруппыОтбора(Знач ОбластьПоиска,
Знач ИмяПоля = Неопределено,
Знач Представление = Неопределено) Экспорт
Если ЗначениеЗаполнено(ИмяПоля) Тогда
ЗначениеПоиска = Новый ПолеКомпоновкиДанных(ИмяПоля);
СпособПоиска = 1;
Иначе
СпособПоиска = 2;
ЗначениеПоиска = Представление;
КонецЕсли;
МассивЭлементов = Новый Массив;
НайтиРекурсивно(ОбластьПоиска.Элементы, МассивЭлементов, СпособПоиска, ЗначениеПоиска);
Возврат МассивЭлементов;
КонецФункции
// Возвращает значение параметра компоновки данных
//
// Параметры:
// ОбъектНастройки - НастройкиКомпоновкиДанных, ПользовательскиеНастройкиКомпоновкиДанных, КомпоновщикНастроекКомпоновкиДанных,
// НастройкиВложенногоОбъектаКомпоновкиДанных, ДанныеРасшифровкиКомпоновкиДанных, КоллекцияЗначенийПараметровКомпоновкиДанных,
// ОформлениеКомпоновкиДанных
// Параметр - Строка, ПараметрКомпоновкиДанных - поле или имя поля, для которого нужно вернуть параметр
//
// Возвращаемое значение:
// ЗначениеПараметраНастроекКомпоновкиДанных - Неопределено, если параметр не найден
//
Функция ПолучитьПараметр(ОбъектНастройки, Параметр) Экспорт
ЗначениеПараметра = Неопределено;
ПолеПараметр = ?(ТипЗнч(Параметр) = Тип("Строка"), Новый ПараметрКомпоновкиДанных(Параметр), Параметр);
Если ТипЗнч(ОбъектНастройки) = Тип("НастройкиКомпоновкиДанных") Тогда
ЗначениеПараметра = ОбъектНастройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ПользовательскиеНастройкиКомпоновкиДанных") Тогда
Для Каждого ЭлементНастройки Из ОбъектНастройки.Элементы Цикл
Если ТипЗнч(ЭлементНастройки) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") И ЭлементНастройки.Параметр = ПолеПараметр Тогда
ЗначениеПараметра = ЭлементНастройки;
Прервать;
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
Для Каждого ЭлементНастройки Из ОбъектНастройки.ПользовательскиеНастройки.Элементы Цикл
Если ТипЗнч(ЭлементНастройки) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") И ЭлементНастройки.Параметр = ПолеПараметр Тогда
ЗначениеПараметра = ЭлементНастройки;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеПараметра = Неопределено Тогда
ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
КонецЕсли;
Если ЗначениеПараметра = Неопределено Тогда
ЗначениеПараметра = ОбъектНастройки.ФиксированныеНастройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
КонецЕсли;
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("НастройкиВложенногоОбъектаКомпоновкиДанных") Тогда
ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ДанныеРасшифровкиКомпоновкиДанных") Тогда
ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("КоллекцияЗначенийПараметровКомпоновкиДанных") Тогда
ЗначениеПараметра = ОбъектНастройки.Найти(ПолеПараметр);
ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ОформлениеКомпоновкиДанных") Тогда
ЗначениеПараметра = ОбъектНастройки.НайтиЗначениеПараметра(ПолеПараметр);
КонецЕсли;
Возврат ЗначениеПараметра;
КонецФункции
// Устанавливает параметр настроек компоновки данных
//
// Параметры:
// Настройки - НастройкиКомпоновкиДанных, ПользовательскиеНастройкиКомпоновкиДанных,
// КомпоновщикНастроекКомпоновкиДанных - настройки КД, для которых требуется установить параметры
// Параметр - Строка, ПараметрКомпоновкиДанных - параметр, который требуется установить
// Значение - Произвольный - значение, которое требуется установить
// Использование - Булево - признак использования параметра КД
//
// Возвращаемое значение:
// ЗначениеПараметраНастроекКомпоновкиДанных - установленный параметр настроек КД. Неопределено, если параметр на найден
//
Функция УстановитьПараметр(Настройки, Параметр, Значение, Использование = Истина) Экспорт
ЗначениеПараметра = ПолучитьПараметр(Настройки, Параметр);
Если ЗначениеПараметра <> Неопределено Тогда
ЗначениеПараметра.Значение = Значение;
ЗначениеПараметра.Использование = Использование;
КонецЕсли;
Возврат ЗначениеПараметра;
КонецФункции
Функция ВключенФильтрПоПеременной(НастройкиОтчета,ИмяПеременной)
//ЭлементыОтбора = ОбщегоНазначенияКлиентСервер.НайтиЭлементыИГруппыОтбора(НастройкиКомпоновщика.Отбор, ИмяПеременной);
ЭлементыОтбора = НайтиЭлементыИГруппыОтбора(НастройкиОтчета.Отбор, ИмяПеременной);
для каждого ЭлементОтбора из ЭлементыОтбора цикл
если ЭлементОтбора.использование и значениезаполнено(ЭлементОтбора.ПравоеЗначение) тогда
возврат истина;
КонецЕсли;
КонецЦикла;
Возврат ложь;
КонецФункции
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка=ложь;
НастройкиОтчета = КомпоновщикНастроек.ПолучитьНастройки();
ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры=ВключенФильтрПоПеременной(НастройкиОтчета,"СкладОстатков")
или ВключенФильтрПоПеременной(НастройкиОтчета,"Номенклатура");
//КомпоновкаДанныхКлиентСервер.УстановитьПараметр(НастройкиОтчета, "ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры", Истина,ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры);
УстановитьПараметр(НастройкиОтчета, "ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры", Истина,
// если использование параметра отключено, то запрос для формирования временной таблицы будет отсутствовать.
ВключенаВременнаяТаблицаТаблицаОстаткиНоменклатуры);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиОтчета, ДанныеРасшифровки);
сообщить(МакетКомпоновки.НаборыДанных.НаборДанных1.Запрос);
КонецПроцедуры
В данном примере запрос к формирования временной таблицы ТаблицаОстаткиНоменклатуры будет выполняться при условии, когда пользователь выбирает фильтр либо по СкладОстатков, либо по Номенклатура.
4. Неожиданное в преобразовании для периодов ВТ
Как то неоднократно сталкивался, не искал, что про это пишут в документации, был бы признателен, если бы кто-нибудь указал. Пример запроса:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура,
ТоварыНаСкладахОстатки.ВНаличииОстаток
поместить ОстаткиНаДата1
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета,{Номенклатура.* как Номенклатура} ) КАК ТоварыНаСкладахОстатки
;
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура,
ОстаткиНаДата1.ВНаличииОстаток Остаток1,
ТоварыНаСкладахОстатки.ВНаличииОстаток Остаток2
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(,{Номенклатура.* как Номенклатура} ) КАК ТоварыНаСкладахОстатки
левое соединение ОстаткиНаДата1 по ОстаткиНаДата1.Номенклатура=ТоварыНаСкладахОстатки.Номенклатура
В параметрах у нас при этом появится предопределенный параметр "Период". Назначим на форме значения параметров Период и ДатаОтчета. В результирующем запросе получаю:
Результат выполнения компоновщика макета
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ВНаличииОстаток
ПОМЕСТИТЬ ОстаткиНаДата1
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&П, ) КАК ТоварыНаСкладахОстатки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ОстаткиНаДата1.ВНаличииОстаток КАК Остаток1,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток2,
ПРЕДСТАВЛЕНИЕССЫЛКИ(ТоварыНаСкладахОстатки.Номенклатура) КАК НоменклатураПредставление,
ТоварыНаСкладахОстатки.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&П, ) КАК ТоварыНаСкладахОстатки
ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиНаДата1 КАК ОстаткиНаДата1
ПО (ОстаткиНаДата1.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура)
То есть у обеих ВТ установлен один период. И он равен параметру Период. Не поможет и то, что во второй ВТ я явно укажу Период:
...
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период,{Номенклатура.* как Номенклатура} ) КАК ТоварыНаСкладахОстатки
...
Результат выполнения компоновщика будет тот же. Решается только в том случае, если я напишу запрос СКД таким образом:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура,
ТоварыНаСкладахОстатки.ВНаличииОстаток
поместить ОстаткиНаДата1
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета,{Номенклатура.* как Номенклатура} ) КАК ТоварыНаСкладахОстатки
;
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура,
ОстаткиНаДата1.ВНаличииОстаток Остаток1,
ТоварыНаСкладахОстатки.ВНаличииОстаток Остаток2
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ЕщеДатаОтчета,{Номенклатура.* как Номенклатура} ) КАК ТоварыНаСкладахОстатки
левое соединение ОстаткиНаДата1 по ОстаткиНаДата1.Номенклатура=ТоварыНаСкладахОстатки.Номенклатура
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ВНаличииОстаток
ПОМЕСТИТЬ ОстаткиНаДата1
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета, ) КАК ТоварыНаСкладахОстатки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ОстаткиНаДата1.ВНаличииОстаток КАК Остаток1,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток2,
ПРЕДСТАВЛЕНИЕССЫЛКИ(ТоварыНаСкладахОстатки.Номенклатура) КАК НоменклатураПредставление,
ТоварыНаСкладахОстатки.Номенклатура.Наименование КАК НоменклатураНаименование
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ЕщеДатаОтчета, ) КАК ТоварыНаСкладахОстатки
ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиНаДата1 КАК ОстаткиНаДата1
ПО (ОстаткиНаДата1.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура)
Все получилось. Но все равно одна засада остается. Если пресловутый предопределенный параметр под названием Период все же вывести на форму и активизировать его, то при последнем варианте запроса у нас все равно старый косяк - у обеих ВТ один Период. Что тут можно посоветовать - поосторожней пользоваться предопределенным параметром Период, поскольку система так и норовит вставить его во все ВТ остатков. Аналогичное можно сказать про предопределенные параметры НачалоПериода и КонецПериода у ВТ Обороты или ОстаткиИОбороты. Я с ними тоже как то натерпелся. То есть, если виртуальных таблиц более одной и у них разные периоды, то предопределенные лучше не трогать.