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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки часто возникает задача распределения товаров по определенным логическим группам, которые в практике называют «сборами», «коробками» или «контейнерами». Например, нам нужно, чтобы в одну группу попадало не более 100 единиц товара, а позиции, превышающие этот лимит, выделялись в отдельную категорию или разбивались на несколько. Рассмотрим, как это можно реализовать средствами платформы 1С:Предприятие.

Постановка задачи

Представим ситуацию: у нас есть список номенклатуры с разным количеством. Нам нужно пронумеровать группы (сборы) так, чтобы:

  1. Мелкие позиции суммировались, пока их общий объем не превысит лимит (например, 100).
  2. Крупные позиции (свыше 100) автоматически попадали в отдельный именной сбор.
  3. Порядок следования позиций сохранялся (принцип FIFO).

Вариант 1: Использование математических выражений в запросе

Если задача стоит упрощенно — просто пометить каждую строку номером группы на основании её собственного количества (без учета накопления от предыдущих строк), мы можем воспользоваться функциями округления и выбора. Проанализируем пример кода, который позволяет быстро разделить данные:


ВЫБРАТЬ
    ТвояТаблица.Номенклатура КАК Номенклатура,
    ТвояТаблица.Количество КАК Количество,
    ВЫБОР
        КОГДА ТвояТаблица.Количество > 100 ТОГДА "сбор_отдельный"
        ИНАЧЕ "сбор_общий"
    КОНЕЦ КАК ТипСбора
ИЗ
    ВременнаяТаблица КАК ТвояТаблица

Однако, как справедливо замечено в обсуждении, такой подход не решает задачу «наполнения контейнера». Чтобы вычислить именно номер сбора, можно применить целочисленное деление. Важно помнить, что в 1С функция ВЫРАЗИТЬ(... КАК ЧИСЛО) выполняет математическое округление. Чтобы имитировать деление нацело (вниз), мы можем вычитать 0.5:


ВЫБРАТЬ
    Номенклатура,
    Количество,
    ВЫРАЗИТЬ((Количество / 100) - 0.5 КАК ЧИСЛО(10, 0)) КАК НомерГруппы
ИЗ
    Данные

Вариант 2: Программный обход результата (Цикл)

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

Рассмотрим алгоритм программного распределения:

  1. Выгружаем результат запроса в Таблицу Значений.
  2. Добавляем колонку НомерСбора.
  3. Перебираем таблицу, накапливая текущую сумму.

ТекущаяСумма = 0;
НомерСбора = 1;
Лимит = 100;

Для Каждого Строка Из ТЗ_Данные Цикл
    Если Строка.Количество > Лимит Тогда
        // Крупный товар всегда в новый сбор
        НомерСбора = НомерСбора + 1;
        Строка.НомерСбора = "Сбор " + НомерСбора;
        ТекущаяСумма = 0; // Сбрасываем счетчик
        НомерСбора = НомерСбора + 1; // Следующий товар пойдет в новый
    ИначеЕсли ТекущаяСумма + Строка.Количество > Лимит Тогда
        // Лимит превышен, переходим к следующему сбору
        НомерСбора = НомерСбора + 1;
        Строка.НомерСбора = "Сбор " + НомерСбора;
        ТекущаяСумма = Строка.Количество;
    Иначе
        // Помещаемся в текущий сбор
        Строка.НомерСбора = "Сбор " + НомерСбора;
        ТекущаяСумма = ТекущаяСумма + Строка.Количество;
    КонецЕсли;
КонецЦикла;

Вариант 3: Современный метод через оконные функции (8.3.24+)

Если ваша платформа обновлена до версии 8.3.24 и выше, мы можем использовать мощь оконных функций. Это позволяет вычислить накопительный итог прямо внутри запроса без использования медленных соединений таблиц.

Разберем, как это выглядит. Мы можем использовать конструкцию СУММА(...) НАД(...). Выясним причину, почему это лучше: производительность оконных функций в разы выше, чем у вложенных запросов.


ВЫБРАТЬ
    Номенклатура,
    Количество,
    СУММА(Количество) НАД (УПОРЯДОЧИТЬ ПО Номенклатура) КАК НакопительныйИтог
ПОМЕСТИТЬ ВТ_СИтогами
ИЗ
    Справочник.Номенклатура;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Номенклатура,
    Количество,
    ВЫРАЗИТЬ((НакопительныйИтог / 100) - 0.5 КАК ЧИСЛО(10, 0)) + 1 КАК НомерСбора
ИЗ
    ВТ_СИтогами

Важный нюанс: Оконные функции позволяют гибко управлять порядком (через УПОРЯДОЧИТЬ ПО), что критично для задач складского учета и логистики.

Рекомендации и выводы

Проанализировав предложенные варианты, можно сделать следующие выводы:

Помните, что «включение головы», о котором упоминалось в дискуссии, часто заключается в выборе самого простого и поддерживаемого решения, а не самого сложного запроса.

← На главную