Как в запросе 1С получить сегмент номенклатуры с максимальным значением скидки или приоритетом

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

При разработке отчетов или механизмов ценообразования в 1С часто возникает задача: если для одного товара определено несколько сегментов или несколько видов скидок, необходимо выбрать только одно значение — самое «сильное» или максимальное. На практике программисты сталкиваются с тем, что стандартная сортировка выдает неожиданные результаты (например, скидка 5% оказывается «больше» 30%). В этой статье мы разберем, почему так происходит, и как правильно составить запрос для получения корректного максимума.

Анализ проблемы: почему автоматическая сортировка подводит

Разберем ситуацию, представленную в исходном запросе. Автор пытается соединить данные о ценах, штрихкодах и сегментах, чтобы получить скидку по РеквизитуДопУпорядочивания. Основная сложность заключается в том, что в типовых конфигурациях (таких как 1С:Управление торговлей 11, ERP 2 или КА 2) дополнительные реквизиты или наименования сегментов часто имеют строковый тип данных.

Проанализируем причину, по которой 5% оказывается выше 30% при обычной сортировке. В базе данных строки сравниваются посимвольно (лексикографически). Поскольку первый символ «5» больше, чем первый символ «3», система считает строку «5%» больше строки «30%». Чтобы избежать этой ошибки, нам необходимо либо использовать числовой реквизит Приоритет, либо приводить данные к числу, либо использовать специальные правила заполнения строковых индексов (с ведущими нулями).

Решение 1: Использование числового приоритета (Штатный механизм)

В типовых конфигурациях 1С для объектов, связанных со скидками и наценками, предусмотрен стандартный реквизит Приоритет. Это числовое поле, специально созданное для управления порядком применения. Вместо того чтобы пытаться анализировать название сегмента или процент в строке, правильнее будет опираться именно на него.

Рассмотрим, как изменить запрос, чтобы получить только ту запись, у которой самый высокий приоритет. Если нам нужно получить данные только для одной конкретной позиции номенклатуры, мы можем использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 с упорядочиванием по убыванию.

Пример кода для одной позиции:


ВЫБРАТЬ ПЕРВЫЕ 1
    НоменклатураСегмента.Сегмент КАК Сегмент,
    Скидки.Приоритет КАК Приоритет
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.СкидкиНаценки КАК Скидки
    ПО НоменклатураСегмента.Сегмент = Скидки.СегментНоменклатурыПредоставления
ГДЕ
    НоменклатураСегмента.Номенклатура = &Номенклатура
УПОРЯДОЧИТЬ ПО
    Приоритет УБЫВ

Решение 2: Получение максимума для списка товаров (Группировка)

Если же перед нами стоит задача вывести список из множества товаров, и для каждого нужно определить свой максимальный сегмент, конструкция ПЕРВЫЕ 1 не подойдет, так как она ограничит весь результат запроса одной строкой. В этом случае мы должны использовать временные таблицы и агрегатные функции.

Выясним алгоритм действий по шагам:

  1. Создадим временную таблицу, в которой соберем все доступные сегменты и скидки для товаров.
  2. Сгруппируем эти данные по Номенклатуре и найдем МАКСИМУМ(Приоритет).
  3. Соединим полученный результат обратно с исходными данными по двум полям: по Номенклатуре и по значению максимального приоритета.

Посмотрим на пример реализации такого запроса:


// Шаг 1: Получаем все сопоставления и их приоритеты
ВЫБРАТЬ
    НоменклатураСегмента.Номенклатура КАК Номенклатура,
    Скидки.Приоритет КАК Приоритет,
    Скидки.Ссылка КАК Скидка
ПОМЕСТИТЬ ВТ_ВсеСкидки
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СкидкиНаценки КАК Скидки
    ПО НоменклатураСегмента.Сегмент = Скидки.СегментНоменклатурыПредоставления;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Находим максимальный приоритет для каждой номенклатуры
ВЫБРАТЬ
    ВТ_ВсеСкидки.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ВТ_ВсеСкидки.Приоритет) КАК МаксПриоритет
ПОМЕСТИТЬ ВТ_МаксимальныеПриоритеты
ИЗ
    ВТ_ВсеСкидки КАК ВТ_ВсеСкидки
СГРУППИРОВАТЬ ПО
    ВТ_ВсеСкидки.Номенклатура;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Выбираем итоговые данные, отсекая лишнее
ВЫБРАТЬ
    ВТ_ВсеСкидки.Номенклатура,
    ВТ_ВсеСкидки.Скидка,
    ВТ_ВсеСкидки.Приоритет
ИЗ
    ВТ_ВсеСкидки КАК ВТ_ВсеСкидки
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксимальныеПриоритеты КАК ВТ_Макс
    ПО ВТ_ВсеСкидки.Номенклатура = ВТ_Макс.Номенклатура
    И ВТ_ВсеСкидки.Приоритет = ВТ_Макс.МаксПриоритет

Важные нюансы работы с сегментами

Проанализируем еще несколько технических моментов, которые критичны для корректной работы этого механизма:

1. Актуальность данных в регистре. Регистр НоменклатураСегмента в типовых конфигурациях может заполняться по-разному в зависимости от настроек сегмента. Если для сегмента установлен способ формирования «Формировать динамически», данные в этом регистре не хранятся постоянно. Они появляются там только после выполнения регламентного задания по обновлению сегментов. Если вы не видите нужных данных в запросе, проверьте, обновлены ли сегменты в базе.

2. Статус и период действия скидок. В реальных условиях недостаточно просто найти максимальную скидку. Нужно убедиться, что она действует в текущий момент. В справочнике СкидкиНаценки обычно есть реквизит Статус (значение перечисления Действует). Также важно проверять даты начала и окончания действия акции, если они используются в вашей системе. Добавьте условие ГДЕ Скидки.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДействияСкидокНаценок.Действует) в ваш запрос.

3. Группы совместного применения. Помните, что логика «выбрать самую большую скидку» может быть сложнее, чем кажется. В 1С существуют группы совместного применения скидок (Минимум, Максимум, Сложение, Вытеснение). Если ваша задача — просто найти информационно самый «весомый» сегмент для номенклатуры, то описанного выше метода с приоритетами достаточно. Если же вы имитируете расчет цены, придется учитывать структуру этих групп.

Рекомендации по заполнению Реквизита доп. упорядочивания

Если вы всё же вынуждены использовать строковое поле РеквизитДопУпорядочивания для сортировки, придерживайтесь правила ведущих нулей. Чтобы строка «5» была меньше строки «30», они должны быть приведены к одинаковой длине: 005 и 030. В этом случае лексикографическая сортировка сработает так же, как числовая. Однако использование числового поля Приоритет всегда является более надежным и производительным решением для платформы 1С:Предприятие 8.3.

Таким образом, мы рассмотрели основные причины ошибок при поиске максимального сегмента и научились строить запрос с использованием временных таблиц и группировок для корректного выбора данных по приоритету.

← На главную