При разработке обработок для работы с прайс-листами или подбором товаров часто возникает ситуация, когда в одну и ту же колонку табличной части могут быть записаны объекты разных типов. Например, это может быть как конкретная Номенклатура, так и целая НоменклатурнаяГруппа. Сложность заключается в том, что логика получения данных (например, артикула поставщика) для этих объектов различается: для группы нужно найти «основную» номенклатуру внутри неё, а для одиночной позиции — взять данные напрямую.
В этой статье мы подробно разберем, как построить эффективный запрос, учитывающий оба типа данных, и как правильно обработать результат, чтобы не допустить ошибок при заполнении табличной части.
Рассмотрим ситуацию: у нас есть табличная часть ВыбраннаяГруппа, где колонка НоменклатурнаяГруппа имеет составной тип. Нам необходимо для каждой строки получить Артикул из подчиненной табличной части справочника Номенклатура, которая называется МТД_Поставщики.
Проблема: Если мы просто будем обращаться через точку Ссылка.НоменклатурнаяГруппа, запрос будет работать корректно только для элементов типа НоменклатурнаяГруппа. Если же в строке указана просто Номенклатура, такое условие не сработает, и мы получим пустой результат или некорректное сопоставление строк.
Самый надежный способ решить задачу — разделить логику получения данных на два независимых блока и объединить их результаты. Проанализируем структуру такого запроса:
НоменклатурнаяГруппа у номенклатуры и обязательно фильтруем по флагу ОсновнаяНоменклатура.Рассмотрим пример кода запроса:
ТекстЗапроса =
"ВЫБРАТЬ
| НоменклатураМТД_Поставщики.Артикул КАК Артикул,
| НоменклатураМТД_Поставщики.Ссылка.НоменклатурнаяГруппа КАК ИдентификаторПоиска,
| NULL КАК ПрямаяСсылка
|ИЗ
| Справочник.Номенклатура.МТД_Поставщики КАК НоменклатураМТД_Поставщики
|ГДЕ
| НоменклатураМТД_Поставщики.Ссылка.НоменклатурнаяГруппа В(&МассивДанных)
| И НоменклатураМТД_Поставщики.Поставщики = &Поставщик
| И НоменклатураМТД_Поставщики.Ссылка.ОсновнаяНоменклатура = ИСТИНА
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| НоменклатураМТД_Поставщики.Артикул,
| NULL,
| НоменклатураМТД_Поставщики.Ссылка
|ИЗ
| Справочник.Номенклатура.МТД_Поставщики КАК НоменклатураМТД_Поставщики
|ГДЕ
| НоменклатураМТД_Поставщики.Ссылка В(&МассивДанных)
| И НоменклатураМТД_Поставщики.Поставщики = &Поставщик";
В данном примере мы добавили вспомогательные колонки ИдентификаторПоиска и ПрямаяСсылка, чтобы на этапе обработки результата точно знать, к какому типу относится найденный артикул.
После выполнения запроса мы получаем таблицу значений. Чтобы быстро сопоставить данные из таблицы с исходной табличной частью формы (особенно если строк много), проанализируем использование индексов. Метод НайтиСтроки работает значительно быстрее, если предварительно проиндексировать колонки поиска.
Разберем по шагам алгоритм заполнения:
ТЗ.ТЗ.Посмотрим на реализацию этого алгоритма:
РезультатЗапроса = Запрос.Выполнить();
ТЗ_Результат = РезультатЗапроса.Выгрузить();
// Индексируем для ускорения поиска в цикле
ТЗ_Результат.Индексы.Добавить("ИдентификаторПоиска");
ТЗ_Результат.Индексы.Добавить("ПрямаяСсылка");
Для Каждого СтрТЧ Из ЭтотОбъект.ВыбраннаяГруппа Цикл
ПараметрыПоиска = Новый Структура;
// Определяем тип и формируем структуру поиска
Если ТипЗнч(СтрТЧ.НоменклатурнаяГруппа) = Тип("СправочникСсылка.НоменклатурныеГруппы") Тогда
ПараметрыПоиска.Вставить("ИдентификаторПоиска", СтрТЧ.НоменклатурнаяГруппа);
Иначе
ПараметрыПоиска.Вставить("ПрямаяСсылка", СтрТЧ.НоменклатурнаяГруппа);
КонецЕсли;
НайденныеСтроки = ТЗ_Результат.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрТЧ.АртикулПоставщика = НайденныеСтроки[0].Артикул;
Иначе
СтрТЧ.АртикулПоставщика = ""; // Очищаем, если данные не найдены
КонецЕсли;
КонецЦикла;
Рассмотрим еще несколько важных моментов, которые помогут сделать ваш код более профессиональным и устойчивым к ошибкам:
1. Использование функции ВЫРАЗИТЬ. При обращении к полям составного типа в запросах (например, Ссылка.НоменклатурнаяГруппа), платформа 1С может генерировать избыточные соединения (JOIN) в SQL-запросе. Чтобы этого избежать, рекомендуется использовать ВЫРАЗИТЬ(Поле КАК Тип). Это явно указывает системе, к какой таблице обращаться.
2. Проверка на пустые значения. Перед передачей массива в запрос всегда проверяйте, заполнен ли он. Если передать пустой массив в параметр В(&Массив), запрос может выполняться долго или вернуть неожиданный результат в зависимости от версии платформы.
3. Временные таблицы. Если строк в табличной части очень много (более 1000), вместо передачи массива в параметры лучше использовать временные таблицы. Сначала выгружаем ТЧ во временную таблицу, индексируем её, а затем выполняем соединение с данными справочников. Это наиболее оптимальный путь с точки зрения производительности СУБД.
4. Обработка дублей. В запросе с ОБЪЕДИНИТЬ ВСЕ могут возникнуть дублирующие записи, если логика условий пересекается. Если вам нужен только один (первый попавшийся) артикул, можно использовать конструкцию ВЫБРАТЬ РАЗЛИЧНЫЕ или сгруппировать результат запроса.
Выясним причину, почему автор в исходной теме столкнулся с трудностями: изначально использовался только один блок ГДЕ, который жестко ограничивал выборку только типом НоменклатурнаяГруппа. Переход к использованию ОБЪЕДИНИТЬ ВСЕ и последующая типизация при поиске в ТЗ позволили корректно обрабатывать «смешанные» списки номенклатуры.
Используя предложенный подход, мы обеспечиваем гибкость системы: пользователь может добавлять в список как отдельные товары, так и целые категории, а программный код корректно найдет актуальные артикулы поставщика для каждого случая.