В практике разработки на платформе 1С часто возникает классическая задача: есть план отгрузок на разные даты и есть общая сумма фактически отгруженного товара. Нам необходимо понять, какие пункты плана уже закрыты фактом, а какие остались невыполненными, причем делать это нужно последовательно (хронологически). В этой статье мы разберем, как реализовать подобный алгоритм распределения, используя только механизмы языка запросов 1С.
Представим ситуацию: у нас есть таблица ВТ_ОтгрузитьПлан с плановыми показателями (например, 10 единиц на 01.01.2025 и 20 единиц на 02.01.2025) и таблица ВТ_Отгружено, где зафиксированы факты отгрузок (например, три записи: 2, 9 и 7 единиц). Общий факт составляет 18 единиц. Нам нужно получить результат, в котором первые 10 единиц факта «закроют» первый день плана, а оставшиеся 8 единиц частично покроют второй день плана.
Проанализируем ситуацию: для решения нам потребуется механизм нарастающего итога. Мы должны знать, сколько единиц плана накопилось к каждой конкретной дате, чтобы сопоставить это с общей суммой отгрузок.
Если ваша платформа версии ниже 8.3.20, основным инструментом для получения нарастающего итога будет левое соединение таблицы самой с собой. Рассмотрим этот метод по шагам.
Шаг 1. Подготовка данных плана и расчет накопленного итога.
Для начала мы создаем временную таблицу ВТ_План. Здесь мы соединяем таблицу плана План с ее копией ПланНакоп по условию, что дата в накоплении меньше текущей даты. Это позволит нам для каждой строки плана получить сумму всех предыдущих планов.
ВЫБРАТЬ
План.Поле1 КАК ДатаПлана,
МАКСИМУМ(План.Поле2) КАК ПланТекущий,
ЕСТЬNULL(СУММА(ПланНакоп.Поле2), 0) КАК ПланНакопленныйРанее
ПОМЕСТИТЬ ВТ_План
ИЗ
ВТ_ОтгрузитьПлан КАК План
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОтгрузитьПлан КАК ПланНакоп
ПО План.Поле1 > ПланНакоп.Поле1
СГРУППИРОВАТЬ ПО
План.Поле1
;
Шаг 2. Агрегация фактических данных.
Так как факт может быть распределен по разным датам, не совпадающим с планом, нам проще всего сначала получить общую сумму отгруженного товара. Проанализируем это в отдельной таблице:
ВЫБРАТЬ
СУММА(Отгружено.Количество) КАК ОбщийФакт
ПОМЕСТИТЬ ВТ_ОтгруженоИтого
ИЗ
ВТ_Отгружено КАК Отгружено
;
Шаг 3. Финальный расчет остатка.
Теперь мы соединяем накопленный план с итоговым фактом и вычисляем, сколько осталось отгрузить по каждой строке. Логика проста: из текущего количества плана вычитаем ту часть факта, которая осталась после закрытия предыдущих периодов.
ВЫБРАТЬ
План.ДатаПлана,
ВЫБОР
КОГДА План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт < 0
ТОГДА 0 -- План полностью закрыт
ИНАЧЕ ВЫБОР
КОГДА План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт > План.ПланТекущий
ТОГДА План.ПланТекущий -- Факт еще не дошел до этого периода
ИНАЧЕ План.ПланТекущий + План.ПланНакопленныйРанее - Итого.ОбщийФакт
КОНЕЦ
КОНЕЦ КАК ОсталосьОтгрузить
ИЗ
ВТ_План КАК План,
ВТ_ОтгруженоИтого КАК Итого
Начиная с версии 8.3.20, в 1С появились оконные функции. Это кардинально меняет подход к расчету нарастающих итогов. Вместо тяжелого соединения ПО План.Дата > ПланНакоп.Дата, которое работает медленно на больших объемах данных, мы можем использовать конструкцию ОБЗОРОМ (в терминах SQL — OVER).
Разберем, как упростится наш запрос:
ВЫБРАТЬ
ДатаПлана,
КоличествоПлан,
СУММА(КоличествоПлан) ОБЗОРОМ (УПОРЯДОЧИТЬ ПО ДатаПлана) - КоличествоПлан КАК ПланНакопленныйРанее
ПОМЕСТИТЬ ВТ_ПланСНакоплением
ИЗ
ВТ_ОтгрузитьПлан
Использование оконных функций позволяет платформе вычислить сумму за один проход по таблице, что значительно повышает производительность системы.
Дата во временной таблице ВТ_ОтгрузитьПлан. Без индекса время выполнения запроса при 1000 строк может вырасти до нескольких секунд.ПАРТИЦИЯ ПО Товар. В случае с самосоединением — добавьте равенство товаров в условие связи: ПО План.Товар = ПланНакоп.Товар И План.Дата > ПланНакоп.Дата.ВЫБОР КОГДА можно использовать формулу: Макс(0, Мин(План_Текущий, План_Накопленный_С_Текущим - Общий_Факт)). Это позволит сделать код более читаемым.Выяснив причину сложности — отсутствие прямого нарастающего итога в старых версиях — мы рассмотрели два эффективных пути решения. Выбор между ними зависит от версии вашей платформы и требований к производительности.