Как в отчете СКД корректно отображать нулевые обороты по проданным сериям товаров и избежать дублирования при новых поступлениях?

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

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

Давайте вместе разберем эту ситуацию и предложим эффективное решение.

Суть проблемы: Некорректное отображение проданных серий и дублирование

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

Ключевые моменты проблемы:

  1. Отображение оборотов проданных серий: Пользователь хочет, чтобы для серий, которых нет в наличии (то есть их остаток равен нулю), поле КоличествоОборот отображалось как ноль.
  2. Дублирование данных: Когда на склад поступает новая партия товара, имеющая, возможно, схожую серию или номенклатуру, отчет начинает показывать старые сроки годности с новыми количествами, и новые сроки годности с новыми количествами, создавая путаницу. Например, товар со сроком годности A (количество 7) и B (количество 12) был продан. При новом поступлении отчета отображается старая серия A с количеством 10 (вместо 7) и старая серия B с количеством 25 (вместо 12), хотя этих товаров фактически нет на складе.
  3. Настройка учета по партиям: В программе настроен учет по партиям, что подразумевает использование серий.

Анализируем текущий запрос автора

Предоставленный автором запрос СКД является достаточно сложным, но мы сосредоточимся на тех его частях, которые касаются получения данных по остаткам и оборотам серий. Запрос объединяет данные из нескольких источников:

Давайте посмотрим на основные моменты, связанные с остатками и оборотами:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ...
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК Остаток1,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.РезервОстаток, 0) КАК Резерв,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) - ЕСТЬNULL(ТоварыНаСкладахОстатки.РезервОстаток, 0) КАК СвободныйОстаток,
    ВЫРАЗИТЬ(NULL КАК ЧИСЛО(15, 2)) КАК Остаток,
    ТоварыНаСкладахОстатки.Склад КАК Склад,
    ...
    ДвиженияСерийТоваровОбороты.Серия КАК Серия,
    ДвиженияСерийТоваровОбороты.Номенклатура КАК Номенклатура1,
    ДвиженияСерийТоваровОбороты.Характеристика КАК Характеристика1,
    ДвиженияСерийТоваровОбороты.Документ КАК Документ,
    ДвиженияСерийТоваровОбороты.Магазин КАК Магазин,
    ДвиженияСерийТоваровОбороты.СкладскаяОперация КАК СкладскаяОперация,
    МАКСИМУМ(ВЫБОР КОГДА ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ОтражениеИзлишков) ИЛИ ... ТОГДА ДвиженияСерийТоваровОбороты.КоличествоОборот ИНАЧЕ 0 КОНЕЦ) КАК Приемка,
    ВЫБОР КОГДА ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ОтгрузкаВРозницу) ИЛИ ... ТОГДА ДвиженияСерийТоваровОбороты.КоличествоОборот ИНАЧЕ 0 КОНЕЦ КАК Отгрузка,
    ...
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
    ...
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДвиженияСерийТоваров.Обороты КАК ДвиженияСерийТоваровОбороты ПО (ДвиженияСерийТоваровОбороты.Номенклатура = СправочникНоменклатура.Ссылка)
    ...

Ключевые наблюдения:

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

Причина проблемы с дублированием:

Основная проблема заключается в том, что ДвиженияСерийТоваровОбороты просто фиксирует факт движения товара по серии. Если серия А была принята (оборот прихода) и затем продана (оборот расхода), обе записи остаются в таблице оборотов за соответствующий период. Если позже товар с *тем же идентификатором серии* (например, тот же срок годности, который является частью наименования серии) поступает вновь, то отчет, построенный только на оборотах, будет показывать все движения по этой "серии", не различая старые и новые поступления, если не использовать актуальные остатки по сериям. Это создает иллюзию, что "старая" серия все еще присутствует или что ее количество изменилось на количество из нового поступления.

Поле Серия в регистре ДвиженияСерийТоваров предназначено для учета конкретных партий, серийных номеров, сроков годности. Если "Серия" не является абсолютно уникальным идентификатором для каждой физической партии товара, а, например, просто хранит срок годности (который может быть одинаковым у разных партий одного и того же товара, но разных по дате производства), то мы столкнемся с проблемой, когда одна и та же "серия" относится к разным физическим товарам.

В данном случае, если "старый срок годности" означает значение поля Серия, то при поступлении нового товара с аналогичным значением Серия, все обороты по этому значению серии будут агрегироваться, что и приводит к некорректным данным.

Предлагаемое решение: Использование регистра "ДвиженияСерийТоваров.Остатки"

Для корректного решения проблемы нам необходимо использовать не только обороты, но и текущие остатки по каждой конкретной серии. Виртуальная таблица РегистрНакопления.ДвиженияСерийТоваров.Остатки предоставляет именно такую информацию – актуальный остаток для каждой уникальной комбинации "Номенклатура", "Характеристика" (если используется) и "Серия" на заданный момент времени (конец периода отчета).

Мы предлагаем следующий подход:

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

Шаги по доработке запроса

Давайте модифицируем ваш запрос, чтобы он учитывал остатки по сериям. Мы сосредоточимся на ключевых таблицах ДвиженияСерийТоваров.Обороты и ДвиженияСерийТоваров.Остатки.

1. Добавляем виртуальную таблицу "ДвиженияСерийТоваров.Остатки"

Для начала, нам нужно добавить эту таблицу в секцию ИЗ вашего запроса и корректно ее соединить.


ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
        ПО (ТоварыНаСкладахОстатки.Номенклатура = СправочникНоменклатура.Ссылка)
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
        ПО (Штрихкоды.Владелец = СправочникНоменклатура.Ссылка)
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
        ПО (ЦеныНоменклатурыСрезПоследних.Номенклатура = СправочникНоменклатура.Ссылка)
        // Исходное соединение с оборотами, мы его немного переделаем
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДвиженияСерийТоваров.Обороты КАК ДвиженияСерийТоваровОбороты
        ПО (ДвиженияСерийТоваровОбороты.Номенклатура = СправочникНоменклатура.Ссылка)
        // **** ДОБАВЛЯЕМ НОВОЕ СОЕДИНЕНИЕ С ОСТАТКАМИ ПО СЕРИЯМ ****
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДвиженияСерийТоваров.Остатки КАК ДвиженияСерийТоваровОстатки
        ПО (ДвиженияСерийТоваровОстатки.Номенклатура = ДвиженияСерийТоваровОбороты.Номенклатура
            И ДвиженияСерийТоваровОстатки.Характеристика = ДвиженияСерийТоваровОбороты.Характеристика
            И ДвиженияСерийТоваровОстатки.Серия = ДвиженияСерийТоваровОбороты.Серия)

Важные моменты соединения:

  1. Мы соединяем ДвиженияСерийТоваровОбороты (как основной источник данных по движениям) с ДвиженияСерийТоваровОстатки.
  2. Соединение производится по всем ключевым измерениям регистра, отвечающим за уникальность серии: Номенклатура, Характеристика (если используется) и Серия. Это гарантирует, что мы получим остаток именно для той конкретной серии, о которой идет речь в строке оборота.
  3. Используем ЛЕВОЕ СОЕДИНЕНИЕ, чтобы даже те обороты по сериям, которые уже не имеют остатка (полностью проданы), все равно присутствовали в выборке (но их остаток будет NULL).

2. Модифицируем поля для отображения количества

Теперь, когда у нас есть доступ к актуальному остатку по серии, мы можем изменить поля Приемка и Отгрузка, а также добавить новое поле, которое будет показывать общее количество оборота, обнуляя его при отсутствии остатка.

В секции ВЫБРАТЬ вашего запроса:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ...
    // Старые поля остатков по номенклатуре, которые могут быть полезны, но не для детализации по сериям
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК ОбщийОстатокНоменклатуры,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.РезервОстаток, 0) КАК ОбщийРезервНоменклатуры,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) - ЕСТЬNULL(ТоварыНаСкладахОстатки.РезервОстаток, 0) КАК ОбщийСвободныйОстатокНоменклатуры,
    ...
    ДвиженияСерийТоваровОбороты.Серия КАК Серия,
    ДвиженияСерийТоваровОбороты.Номенклатура КАК Номенклатура1,
    ДвиженияСерийТоваровОбороты.Характеристика КАК Характеристика1,
    ...
    ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) КАК ТекущийОстатокСерии, // Вот оно! Актуальный остаток по конкретной серии
    
    // Модифицированное поле Приемка: показываем оборот только если есть остаток по серии
    ВЫБОР
        КОГДА ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) > 0 И (
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ОтражениеИзлишков) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаКомплектующихПослеРазборки) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаОтПоставщика) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаПоВозвратуОтКлиента) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаПоПеремещению) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаПоПрочемуОприходованию) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ПриемкаСобранныхКомплектов) ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ВводОстатков) И ДвиженияСерийТоваровОбороты.КоличествоОборот > 0 ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.ОтражениеРезультатовПересчетов) И ДвиженияСерийТоваровОбороты.КоличествоОборот > 0 ИЛИ
            ДвиженияСерийТоваровОбороты.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.Пересчет) И ДвиженияСерийТоваровОбороты.КоличествоОборот > 0
        ) ТОГДА ДвиженияСерийТоваровОбороты.КоличествоОборот
        ИНАЧЕ 0
    КОНЕЦ КАК Приемка,

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

    // Новое поле для общего количества оборота, показываем 0, если нет остатка по серии
    ВЫБОР
        КОГДА ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) = 0
            ТОГДА 0
        ИНАЧЕ ДвиженияСерийТоваровОбороты.КоличествоОборот
    КОНЕЦ КАК КоличествоОборотКорректированное,
    // Можем также показать СвободныйОстаток по серии, если РезервОстаток тоже нужен по серии
    ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) - ЕСТЬNULL(ДвиженияСерийТоваровОстатки.РезервОстаток, 0) КАК СвободныйОстатокСерии
    ...

Важно:

  1. Мы используем ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0), чтобы в случае отсутствия остатка (из-за ЛЕВОГО СОЕДИНЕНИЯ) поле принимало значение 0, а не NULL.
  2. В полях Приемка и Отгрузка мы добавили условие ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) > 0, чтобы показывать обороты только для тех серий, у которых есть остаток. Если вы хотите видеть все обороты, но с пометкой, что серия продана, это условие можно убрать, но тогда поле КоличествоОборотКорректированное будет более информативным.
  3. Обратите внимание, что для Отгрузка мы берем -ДвиженияСерийТоваровОбороты.КоличествоОборот, так как обороты расхода в регистрах часто записываются с отрицательным знаком.

3. Корректируем группировку

После добавления нового поля ТекущийОстатокСерии и его использования в вычисляемых полях, необходимо убедиться, что оно также включено в секцию СГРУППИРОВАТЬ ПО, если вы используете РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ или агрегатные функции, и это поле не является результатом агрегации.

Ваша секция СГРУППИРОВАТЬ ПО должна включать все поля, которые не являются агрегатными функциями. Убедитесь, что ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0) и другие новые поля присутствуют в группировке.


СГРУППИРОВАТЬ ПО
    СправочникНоменклатура.Ссылка,
    ...
    ДвиженияСерийТоваровОбороты.Серия,
    ДвиженияСерийТоваровОбороты.Характеристика,
    ДвиженияСерийТоваровОбороты.Документ,
    ДвиженияСерийТоваровОбороты.Магазин,
    ДвиженияСерийТоваровОбороты.СкладскаяОперация,
    ЕСТЬNULL(ДвиженияСерийТоваровОстатки.КоличествоОстаток, 0), // Добавляем в группировку!
    ЕСТЬNULL(ДвиженияСерийТоваровОстатки.РезервОстаток, 0),
    ...

Если вы хотите агрегировать данные таким образом, чтобы каждая строка отчета соответствовала уникальной серии, а не каждому обороту, вам, возможно, потребуется более тщательно настроить группировки и агрегатные функции. Например, если вы хотите суммировать все обороты по приемке и отгрузке для конкретной серии, вам понадобится использовать СУММА() для полей Приемка и Отгрузка и группировать только по Номенклатура, Характеристика, Серия и ТекущийОстатокСерии.

Настройка СКД для отображения

После изменения запроса в СКД:

  1. Обновите схему: В редакторе СКД обновите схему, чтобы новые поля (например, ТекущийОстатокСерии, КоличествоОборотКорректированное) стали доступны.
  2. Выберите поля: Добавьте новые поля в выбранные поля отчета.
  3. Настройте ресурсы: Если вы используете СКД для агрегации, настройте ресурсы. Например, для Приемка, Отгрузка, КоличествоОборотКорректированное укажите тип агрегатной функции СУММА.
  4. Группировки в СКД: В настройках отчета (в разделе "Настройки") установите необходимые группировки. Например, вы можете сгруппировать по Номенклатура, затем по Серия.
  5. Условное форматирование: Дополнительно вы можете использовать условное форматирование в СКД, чтобы выделить строки с проданными сериями (например, если ТекущийОстатокСерии = 0, покрасить строку серым цветом).

Решение проблемы с "дублированием" старых серий

Причина, по которой "старый срок годности с новым поступлением по количеству" может появляться, связана с тем, что идентификатор Серия в регистре может быть не уникальным для *каждого физического поступления*, а скорее для *вида серии* (например, просто срок годности). Если в систему поступает две разные партии одного и того же товара, но с одинаковым сроком годности, то обе эти партии будут иметь одинаковое значение в поле Серия.

Чтобы избежать такой путаницы, вы можете рассмотреть следующие подходы:

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

В контексте предоставленного запроса, если проблема с дублированием связана именно с тем, что одно и то же значение Серия используется для разных физических партий, то наше предложенное решение с ДвиженияСерийТоваровОстатки частично поможет, показывая 0, если остатка нет. Однако, если есть несколько партий с одинаковой "серией" и *разными* остатками, то объединение полей Номенклатура, Характеристика, Серия все равно может схлопнуть их в одну строку. В этом случае вам нужно будет добавить дополнительное уникальное измерение в регистр (например, "ИдентификаторПартии") или использовать более сложный анализ оборотов.

Давайте подытожим, что мы изменили в запросе:

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

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

← На главную