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

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

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

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

Представим ситуацию: у нас есть таблица ВТ_ОтгрузитьПлан с плановыми показателями (например, 10 единиц на 01.01.2025 и 20 единиц на 02.01.2025) и таблица ВТ_Отгружено, где зафиксированы факты отгрузок (например, три записи: 2, 9 и 7 единиц). Общий факт составляет 18 единиц. Нам нужно получить результат, в котором первые 10 единиц факта «закроют» первый день плана, а оставшиеся 8 единиц частично покроют второй день плана.

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

Способ 1. Классическое решение через самосоединение временных таблиц

Если ваша платформа версии ниже 8.3.20, основным инструментом для получения нарастающего итога будет левое соединение таблицы самой с собой. Рассмотрим этот метод по шагам.

Шаг 1. Подготовка данных плана и расчет накопленного итога.

Для начала мы создаем временную таблицу ВТ_План. Здесь мы соединяем таблицу плана План с ее копией ПланНакоп по условию, что дата в накоплении меньше текущей даты. Это позволит нам для каждой строки плана получить сумму всех предыдущих планов.


ВЫБРАТЬ
    План.Поле1 КАК ДатаПлана,
    МАКСИМУМ(План.Поле2) КАК ПланТекущий,
    ЕСТЬNULL(СУММА(ПланНакоп.Поле2), 0) КАК ПланНакопленныйРанее
ПОМЕСТИТЬ ВТ_План
ИЗ
    ВТ_ОтгрузитьПлан КАК План
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОтгрузитьПлан КАК ПланНакоп
    ПО План.Поле1 > ПланНакоп.Поле1
СГРУППИРОВАТЬ ПО
    План.Поле1
;

Шаг 2. Агрегация фактических данных.

Так как факт может быть распределен по разным датам, не совпадающим с планом, нам проще всего сначала получить общую сумму отгруженного товара. Проанализируем это в отдельной таблице:


ВЫБРАТЬ
    СУММА(Отгружено.Количество) КАК ОбщийФакт
ПОМЕСТИТЬ ВТ_ОтгруженоИтого
ИЗ
    ВТ_Отгружено КАК Отгружено
;

Шаг 3. Финальный расчет остатка.

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


ВЫБРАТЬ
    План.ДатаПлана,
    ВЫБОР
        КОГДА План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт < 0
            ТОГДА 0 -- План полностью закрыт
        ИНАЧЕ ВЫБОР
            КОГДА План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт > План.ПланТекущий
                ТОГДА План.ПланТекущий -- Факт еще не дошел до этого периода
            ИНАЧЕ План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт
        КОНЕЦ
    КОНЕЦ КАК ОсталосьОтгрузить
ИЗ
    ВТ_План КАК План,
    ВТ_ОтгруженоИтого КАК Итого

Способ 2. Современный подход с использованием оконных функций

Начиная с версии 8.3.20, в 1С появились оконные функции. Это кардинально меняет подход к расчету нарастающих итогов. Вместо тяжелого соединения ПО План.Дата > ПланНакоп.Дата, которое работает медленно на больших объемах данных, мы можем использовать конструкцию ОБЗОРОМ (в терминах SQL — OVER).

Разберем, как упростится наш запрос:


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

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

Важные нюансы и рекомендации

  1. Индексирование: Если вы используете Способ 1 (через самосоединение), обязательно создавайте индекс по полю Дата во временной таблице ВТ_ОтгрузитьПлан. Без индекса время выполнения запроса при 1000 строк может вырасти до нескольких секунд.
  2. Добавление аналитики (Товар, Склад): В реальных задачах план всегда строится в разрезе товаров. При использовании оконных функций не забудьте добавить секцию ПАРТИЦИЯ ПО Товар. В случае с самосоединением — добавьте равенство товаров в условие связи: ПО План.Товар = ПланНакоп.Товар И План.Дата > ПланНакоп.Дата.
  3. Математическая формула: Для упрощения условий ВЫБОР КОГДА можно использовать формулу: Макс(0, Мин(План_Текущий, План_Накопленный_С_Текущим - Общий_Факт)). Это позволит сделать код более читаемым.
  4. Обработка превышения: Проанализируйте ситуацию, когда факт превышает общий план. В текущих примерах такая отгрузка просто «исчезнет», так как мы считаем остатки плана. Если вам нужно видеть «перевыполнение», потребуется дополнительная логика в финальном пакете запроса.

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

← На главную