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

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

Начнем разбираться с одной из частых задач при работе с данными в системе 1С: как корректно получить общее количество товаров, или, как принято говорить в учете, конечный остаток, учитывая все движения поступления и расхода. Многие разработчики сталкиваются с тем, что их запросы возвращают множество строк с одним и тем же товаром, когда требуется всего лишь одна сводная строка с итоговым количеством. Давайте вместе проанализируем эту проблему и найдем оптимальные решения.

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

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

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


ВЫБРАТЬ
    ТоварыНаСкладах.НомерСтроки,
    ТоварыНаСкладах.Период КАК Период,
    ТоварыНаСкладах.ВидДвижения,
    ТоварыНаСкладах.Склад КАК Склад,
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    СУММА(ТоварыНаСкладах.Количество) КАК Количество,
    ТоварыНаСкладах.Номенклатура.БазоваяЕдиницаИзмерения КАК ЕдиницаИзмерения
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах

СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура,
    ТоварыНаСкладах.НомерСтроки,
    ТоварыНаСкладах.Период,
    ТоварыНаСкладах.ВидДвижения,
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура.БазоваяЕдиницаИзмерения

УПОРЯДОЧИТЬ ПО
    Склад,
    Номенклатура,
    Период
АВТОУПОРЯДОЧИВАНИЕ

Как мы можем заметить, в данном запросе в секции ВЫБРАТЬ и, что более важно, в секции СГРУППИРОВАТЬ ПО присутствуют такие поля, как НомерСтроки, Период, ВидДвижения и Склад, помимо самой Номенклатура и БазоваяЕдиницаИзмерения. Именно это является основной причиной проблемы.

Давайте разберемся с принципами работы оператора СГРУППИРОВАТЬ ПО в запросах 1С. Когда мы используем агрегатные функции, такие как СУММА, МАКСИМУМ, МИНИМУМ, КОЛИЧЕСТВО, система управления базами данных (СУБД) не просто суммирует все значения. Она собирает строки в группы. Каждая группа формируется на основе уникальных комбинаций значений всех полей, перечисленных в секции СГРУППИРОВАТЬ ПО. После формирования групп, агрегатная функция применяется к значениям в каждой отдельной группе.

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

Попытка убрать все поля из ВЫБРАТЬ, кроме Номенклатура и СУММА(Количество), и оставить в СГРУППИРОВАТЬ ПО только ТоварыНаСкладах.Номенклатура, привело бы к ошибке: "ПОЛЕ НЕ ВХОДИТ В ГРУППУ ТоварыНаСкладах.НомерСтроки" (или любое другое поле, не агрегированное и не в группировке). Это происходит потому, что все поля в секции ВЫБРАТЬ, которые не являются результатом агрегатных функций, обязательно должны быть включены в секцию СГРУППИРОВАТЬ ПО. Мы хотим одну строку по номенклатуре, но при этом выбираем множество полей, которые делают каждую строку уникальной.

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

Решение 1: Использование виртуальной таблицы "Остатки" регистра накопления

Когда мы говорим о получении конечных остатков ресурсов (в нашем случае, количества товаров) на определенный момент времени, наиболее эффективным, производительным и, главное, корректным способом в системе 1С является использование виртуальной таблицы "Остатки" регистров накопления. Это ключевой механизм платформы, специально разработанный для таких задач.

Регистры накопления в 1С предназначены для хранения информации о движениях (приходах и расходах) и быстрых расчетов остатков или оборотов ресурсов по заданным измерениям. Виртуальные таблицы — это специальные представления данных регистра, которые формируются системой "на лету" при выполнении запроса и предоставляют уже агрегированные данные.

Давайте разберем принцип работы с виртуальной таблицей Остатки по шагам:

  1. Мы обращаемся к виртуальной таблице Остатки нужного регистра накопления. В нашем случае это регистр ТоварыНаСкладах, поэтому полное имя виртуальной таблицы будет РегистрНакопления.ТоварыНаСкладах.Остатки.
  2. В запросе мы можем указать параметр Период, чтобы получить остатки на конкретную дату или момент времени. Например, если мы хотим получить остатки на конец дня, мы передадим соответствующую дату и время. Если параметр Период не указан, система вернет остатки на текущий момент выполнения запроса. Это очень удобно для оперативного получения актуальных данных.
  3. Результатом работы этой виртуальной таблицы будет таблица, содержащая для каждой уникальной комбинации измерений регистра (в нашем случае это Номенклатура и Склад) одно агрегированное значение остатка. Этот остаток уже представляет собой корректно рассчитанную разницу между всеми приходами и расходами, произошедшими до указанного периода. Система 1С сама обрабатывает логику "приход минус расход".
  4. Важным преимуществом является то, что при использовании виртуальной таблицы Остатки нам нет необходимости самостоятельно формировать секцию СГРУППИРОВАТЬ ПО для этих измерений, так как данные уже представлены в агрегированном виде.

Давайте посмотрим на пример запроса с виртуальной таблицей Остатки, который решит нашу задачу:


ВЫБРАТЬ
    ТоварыНаСкладахОстатки.Номенклатура,
    ТоварыНаСкладахОстатки.Склад,
    ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
// Если нам нужны остатки на определенную дату, мы можем использовать параметр виртуальной таблицы:
// ГДЕ ТоварыНаСкладахОстатки.Период = &КонецПериода 
// где &КонецПериода - это параметр запроса, содержащий нужную дату

В этом запросе мы выбираем непосредственно измерения Номенклатура и Склад из таблицы ТоварыНаСкладахОстатки, а также ресурс КоличествоОстаток. В результате мы получим ровно одну строку для каждой уникальной комбинации номенклатуры и склада, с уже корректно вычисленным конечным остатком. Этот подход является стандартным и наиболее производительным для задач получения остатков в 1С, особенно для больших объемов данных.

Решение 2: Условное суммирование с помощью оператора "ВЫБОР КОГДА"

Хотя виртуальная таблица Остатки является предпочтительным решением для получения конечного сальдо, существуют ситуации, когда нам может потребоваться более гибкий или детализированный подход. Например, если мы хотим одновременно получить не только остатки, но и обороты, или если логика учета движений более сложная и требует ручной обработки. В таких случаях можно использовать метод условного суммирования с помощью оператора ВЫБОР КОГДА внутри агрегатной функции СУММА.

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

Давайте рассмотрим принцип работы условного суммирования по шагам:

  1. В запросе мы выбираем все движения регистра накопления, то есть обращаемся непосредственно к таблице РегистрНакопления.ТоварыНаСкладах, а не к его виртуальной таблице.
  2. Внутри агрегатной функции СУММА мы используем конструкцию ВЫБОР КОГДА. Эта конструкция позволяет нам проверять значение поля ВидДвижения для каждой записи.
  3. Если ВидДвижения соответствует приходу (в 1С это обычно ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)), то мы включаем значение поля Количество в сумму как положительное число.
  4. Если же ВидДвижения соответствует расходу, то мы включаем значение поля Количество в сумму как отрицательное число, фактически вычитая его (например, ИНАЧЕ -ТоварыНаСкладах.Количество).
  5. Все эти условные значения затем суммируются для каждой группы, определенной в секции СГРУППИРОВАТЬ ПО. В этом случае нам обязательно нужно указать измерения, по которым мы хотим получить остаток (например, Номенклатура и Склад).

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


ВЫБРАТЬ
    ТоварыНаСкладах.Номенклатура,
    ТоварыНаСкладах.Склад,
    СУММА(ВЫБОР
            КОГДА ТоварыНаСкладах.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
                ТОГДА ТоварыНаСкладах.Количество
            ИНАЧЕ -ТоварыНаСкладах.Количество
        КОНЕЦ) КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура,
    ТоварыНаСкладах.Склад

Этот метод также корректно вычисляет конечный остаток (приход минус расход) для каждой номенклатуры на каждом складе. Однако, как мы уже упоминали, он может быть менее производительным для очень больших объемов данных по сравнению с использованием виртуальной таблицы Остатки. Это связано с тем, что данный запрос вынужден обрабатывать каждую запись движения регистра, в то время как виртуальная таблица Остатки использует специализированные индексы и механизмы для быстрого получения агрегированных данных. Мы рекомендуем применять этот подход, когда вам нужна более сложная логика, которую невозможно реализовать только средствами виртуальных таблиц, или когда нужно одновременно получить детальные движения и их сальдо в одном запросе.

Заключение

Мы выяснили, что для успешного получения корректного конечного остатка товаров по номенклатуре в запросах 1С критически важно понимать принципы группировки и уметь правильно выбирать инструменты платформы. Основной и наиболее эффективный инструмент для этой задачи — виртуальная таблица Остатки регистра накопления. Она позволяет получить агрегированные данные остатков быстро и без необходимости сложной ручной обработки движений.

В качестве альтернативы, особенно для специфических сценариев, мы рассмотрели метод условного суммирования с оператором ВЫБОР КОГДА. Этот подход предоставляет больше контроля над логикой расчета, но может быть менее производительным. Мы рекомендуем вам всегда начинать с виртуальной таблицы Остатки для задач получения остатков, и только если она не удовлетворяет вашим специфическим требованиям, переходить к более сложным конструкциям.

Помните: правильный выбор инструмента и понимание его работы — залог эффективной и корректной разработки в 1С. Мы надеемся, что наш разбор поможет вам создавать более точные и производительные запросы для учета товарных остатков.

← На главную