При разработке обработок автоматического заполнения документов (например, «Приобретение товаров и услуг» в УТ 11) часто возникает задача: выбрать не всю номенклатуру из группы, а лишь ограниченное количество из каждой вложенной подгруппы. Основная сложность здесь заключается в иерархической структуре справочника Номенклатура и требовании выполнить задачу одним запросом, без использования запросов в цикле, что является стандартом качественной разработки на платформе 1С.
В этой статье мы разберем, как реализовать такую логику, проанализируем подходы с использованием временных таблиц и функции АВТОНОМЕРЗАПИСИ, а также рассмотрим более современные методы, доступные в новых версиях платформы.
Разберем ситуацию: у нас есть корневая группа (папка), в которой может быть множество подгрупп любого уровня вложенности. Нам необходимо зайти в каждую «конечную» папку и взять оттуда до 10 элементов (если элементов меньше 10 — берем все, что есть). Если делать это в цикле по папкам, производительность системы значительно упадет, особенно при большом количестве номенклатуры. Поэтому мы будем использовать временные таблицы.
Сначала нам необходимо определить список всех групп, которые входят в выбранную иерархию. Рассмотрим первый шаг — получение всех родительских групп:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ВсеГруппы
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В ИЕРАРХИИ(&КорневаяГруппа)
И Номенклатура.ЭтоГруппа = ИСТИНА
В этом запросе мы используем оператор В ИЕРАРХИИ, чтобы получить все папки на любую глубину вложенности. Результат сохраняем во временную таблицу ВТ_ВсеГруппы.
Поскольку в классическом языке запросов 1С (до версии платформы 8.3.20) нет встроенной функции «получить первые N в группе», мы можем использовать хитрость с функцией АВТОНОМЕРЗАПИСИ(). Эта функция доступна только при помещении данных во временную таблицу. Идея заключается в том, чтобы пронумеровать все товары, предварительно упорядочив их по группам.
Рассмотрим последовательность действий:
АВТОНОМЕРЗАПИСИ().
ВЫБРАТЬ
Номенклатура.Родитель КАК Группа,
Номенклатура.Ссылка КАК Товар,
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки
ПОМЕСТИТЬ ВТ_НоменклатураСНомерами
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Родитель В (ВЫБРАТЬ Ссылка ИЗ ВТ_ВсеГруппы)
И Номенклатура.ЭтоГруппа = ЛОЖЬ
И Номенклатура.ПометкаУдаления = ЛОЖЬ
УПОРЯДОЧИТЬ ПО
Группа,
Товар
Теперь у нас есть таблица, где все товары идут по порядку групп, и каждая строка имеет свой уникальный номер. Однако этот номер сквозной по всей таблице, а не начинается заново для каждой группы. Выясним, как это исправить.
Чтобы получить «первые 10 в каждой группе», нам нужно знать минимальный номер записи для каждой группы в нашей временной таблице ВТ_НоменклатураСНомерами. Разберем по шагам дальнейшую логику:
ВЫБРАТЬ
ВТ.Группа КАК Группа,
МИНИМУМ(ВТ.НомерСтроки) КАК НачальныйНомер
ПОМЕСТИТЬ ВТ_СтартыГрупп
ИЗ
ВТ_НоменклатураСНомерами КАК ВТ
ГРУППИРОВАТЬ ПО
ВТ.Группа;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Группа,
ВТ.Товар
ИЗ
ВТ_НоменклатураСНомерами КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СтартыГрупп КАК Старты
ПО ВТ.Группа = Старты.Группа
ГДЕ
ВТ.НомерСтроки МЕЖДУ Старты.НачальныйНомер И Старты.НачальныйНомер + 9
Этот метод позволяет за один проход получить нужные данные. Мы используем условие МЕЖДУ, чтобы ограничить выборку диапазоном от первого найденного номера до десятого.
При работе с конфигурацией Управление торговлей 11 недостаточно просто выбрать ссылку на номенклатуру. Документ «Приобретение товаров и услуг» требует заполнения дополнительных реквизитов. Рассмотрим, что еще нужно учесть:
Номенклатура.ЕдиницаИзмерения.Если ваша база работает на современной версии платформы, решение задачи значительно упрощается. Нам больше не нужны три временные таблицы и манипуляции с АВТОНОМЕРЗАПИСИ. Мы можем использовать функцию ПОРЯДКОВЫЙ_НОМЕР().
Рассмотрим пример запроса с оконной функцией:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
Номенклатура.Родитель КАК Группа,
ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕМЛЮЩИЙ (
РАЗДЕЛИТЬ ПО Номенклатура.Родитель
УПОРЯДОЧИТЬ ПО Номенклатура.Ссылка
) КАК НомерВГруппе
ПОМЕСТИТЬ ВТ_Итоговая
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Родитель В ИЕРАРХИИ(&КорневаяГруппа)
И Номенклатура.ЭтоГруппа = ЛОЖЬ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Товар,
ВТ.Группа
ИЗ
ВТ_Итоговая КАК ВТ
ГДЕ
ВТ.НомерВГруппе <= 10
Использование конструкции РАЗДЕЛИТЬ ПО позволяет платформе автоматически сбрасывать счетчик при смене родителя, что идеально подходит для нашей задачи. Это наиболее производительный и читаемый вариант кода.
При создании обработки не забудьте добавить на форму возможность выбора Контрагента и Организации, так как они являются обязательными для документа ПриобретениеТоваровИУслуг. После выполнения запроса в коде обработки вам останется только создать объект документа и заполнить его табличную часть в цикле по результату запроса:
НовыйДокумент = Документы.ПриобретениеТоваровИУслуг.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Объект.Организация;
НовыйДокумент.Поставщик = Объект.Контрагент;
// ... заполнение остальных реквизитов
Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
НоваяСтрока = НовыйДокумент.Товары.Добавить();
НоваяСтрока.Номенклатура = СтрокаРезультата.Товар;
НоваяСтрока.Количество = 1; // Или другое дефолтное значение
КонецЦикла;
НовыйДокумент.Записать();
Таким образом, мы выяснили, что даже сложные задачи по иерархической выборке можно решить элегантно с помощью механизмов временных таблиц или современных оконных функций 1С, обеспечивая высокую скорость работы системы и чистоту кода.