Как распределить текущие остатки по документам поступления (FIFO) в запросе СКД?

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

В практике разработчика 1С часто возникает задача: на основе типового регистра (например, ТоварыНаСкладах), в котором нет партионного учета, вывести отчет по остаткам в разрезе документов поступления. Это необходимо для анализа возраста склада, определения конкретных поставщиков текущего товара или расчета себестоимости по методу FIFO. Поскольку система не хранит прямую связь «остаток — документ прихода», нам необходимо вычислить эту связь математически в самом запросе.

Рассмотрим подробнее несколько эффективных способов решения этой задачи: от классического соединения таблиц до использования современных оконных функций платформы 8.3.20+.

Метод 1. Обратный нарастающий итог (классический подход)

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

Разберем алгоритм по шагам:

  1. Получаем текущий общий остаток по номенклатуре из виртуальной таблицы Остатки.
  2. Получаем все обороты (поступления) по этой номенклатуре за разумный период.
  3. Для каждого поступления вычисляем нарастающий итог количества прихода в обратном хронологическом порядке.
  4. Сравниваем этот итог с текущим остатком.

Если нарастающая сумма приходов меньше или равна остатку, значит, всё количество по данному документу находится на складе. Если нарастающая сумма приходов предыдущих документов была меньше остатка, а вместе с текущим документом стала больше — значит, этот документ является «граничным», и из него на складе осталась только часть товара.

Метод 2. Реализация через соединение по неравенству в запросе

Посмотрим на пример реализации нарастающего итога в языке запросов. Для этого мы соединим таблицу приходов (Т1) с ней же самой (Т2). Условие связи будет включать неравенство дат, чтобы для каждой записи Т1 получить сумму всех записей Т2, которые были позже неё.


ВЫБРАТЬ
    Приходы1.Номенклатура КАК Номенклатура,
    Приходы1.Регистратор КАК ДокументПрихода,
    Приходы1.Количество КАК КоличествоПриход,
    СУММА(Приходы2.Количество) КАК НарастающийИтогСверху
ИЗ
    ВТ_Приходы КАК Приходы1
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Приходы КАК Приходы2
        ПО Приходы1.Номенклатура = Приходы2.Номенклатура
            И Приходы1.Период <= Приходы2.Период
ГРУППИРОВАТЬ ПО
    Приходы1.Номенклатура,
    Приходы1.Регистратор,
    Приходы1.Количество

Важный момент: такие соединения имеют квадратичную сложность O(n^2). Если документов поступления тысячи, запрос будет выполняться крайне медленно. Рекомендуем предварительно ограничивать таблицу приходов периодом (например, последние полгода), если оборачиваемость товара это позволяет.

Метод 3. Использование оконных функций (Платформа 8.3.20+)

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

Проанализируем пример запроса с оконной функцией:


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

Здесь ПАРТИЦИЯ ПО группирует данные по товару, а УПОРЯДОЧИТЬ ПО ... УБЫВ заставляет систему считать сумму от новых документов к старым. Полученный результат останется только сравнить с текущим остатком в основном запросе или в вычисляемых полях СКД.

Метод 4. Расчет остатка через разницу «Приход - Расход»

Выясним причину, по которой этот метод был предложен в обсуждении. Если в системе ведется учет расходов в разрезе документов оприходования (даже если это не полноценные партии, а просто аналитика), то остаток по документу вычисляется как:

ОстатокПоПриходу = КоличествоПрихода - СуммаРасходовПоЭтомуПриходу

Однако, если такой аналитики в расходах нет, этот метод трансформируется в задачу «распределения общего расхода по приходам». Мы берем общий накопленный расход за все время и «гасим» им приходы в хронологическом порядке. То, что осталось не «погашенным», и есть наш текущий остаток по FIFO.

Особенности работы в СКД

В Системе Компоновки Данных мы можем вынести часть логики в Вычисляемые поля или Ресурсы. Например, функцию нарастающего итога можно реализовать через ВычислитьВыражение:

ВычислитьВыражение("Сумма(КоличествоПриход)", "Период", , "Текущая", "Последняя")

Это позволяет строить отчет динамически. Однако помните о следующих нюансах:

  1. Частичное вхождение документа: Обязательно добавьте условие ВЫБОР, чтобы правильно рассчитать количество в остатке для «граничного» документа. Если НарастающийИтог > ТекущийОстаток, то КоличествоВРезультирующемОстатке = ТекущийОстаток - (НарастающийИтог - КоличествоПрихода).
  2. Характеристики и серии: Если товар учитывается в разрезе серий, они должны быть обязательным полем связи. Иначе остаток перепутается между разными партиями реального товара.
  3. Отрицательные остатки: Если на складе возник «минус», алгоритм FIFO может выдать некорректные данные. Проверьте, чтобы текущий остаток был больше нуля перед распределением.
  4. Пустые приходы: Не забудьте включить в выборку не только документы ПоступлениеТоваровУслуг, но и ОприходованиеИзлишков, ПеремещениеТоваров (если нужно видеть приход на конкретный склад) и СборкаТоваров.

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

← На главную