В практике разработчика 1С часто возникает задача распределения имеющихся складских остатков по текущим потребностям производства. Типичный сценарий: есть список изделий, которые нужно произвести, есть их спецификации (состав материалов) и есть общее количество комплектующих на складе. Нам необходимо не просто соединить таблицы, а «размазать» остатки по списку изделий в порядке очереди (логика FIFO), чтобы понять, на какие изделия материалов хватает полностью, на какие частично, а на какие — вовсе нет.
В этой статье мы подробно разберем, как реализовать подобный расчет внутри одного запроса, не прибегая к медленной обработке результата в цикле на встроенном языке. Мы проанализируем современные методики, включая оконные функции и механизмы нарастающего итога.
Представим, что у нас есть следующие данные:
Наша цель — получить итоговую таблицу, где в каждой строке будет указано, сколько единиц из общего остатка «досталось» конкретному изделию. Рассмотрим, как это сделать наиболее эффективно.
Это самый современный и производительный способ решения задачи. Оконные функции позволяют вычислить нарастающий итог одной строкой, не нагружая сервер базы данных сложными соединениями. Проанализируем, как строится такой запрос.
Сначала мы рассчитываем общую потребность в материале нарастающим итогом. Это покажет нам, сколько всего единиц материала потребуется для обеспечения текущего и всех предыдущих изделий в очереди.
ВЫБРАТЬ
Т.Изделие,
Т.Материал,
Т.КоличествоПоСпецификации,
СУММА(Т.КоличествоПоСпецификации) ОБЪЕДИНЯТЬ (УПОРЯДОЧИТЬ ПО Т.Приоритет, Т.Ссылка) КАК НарастающаяПотребность
ПОМЕСТИТЬ ВТ_ПотребностьСИтогом
ИЗ
ВТ_Спецификации КАК Т
;
Обратите внимание на секцию УПОРЯДОЧИТЬ ПО. Для корректного распределения крайне важен детерминированный порядок. Мы должны четко определить, кто стоит в очереди первым. Обычно для этого используются поля приоритета, даты документа или ссылки.
Если ваша конфигурация работает на платформе версии ниже 8.3.15, оконные функции недоступны. В этом случае мы используем классический метод — соединение таблицы самой с собой. Рассмотрим этот алгоритм подробнее.
Идея заключается в том, чтобы для каждой строки таблицы потребностей найти все строки этой же таблицы, которые «выше» или равны ей по порядку, и просуммировать их значения.
ВЫБРАТЬ
Т1.Изделие КАК Изделие,
Т1.Материал КАК Материал,
Т1.КоличествоПоСпецификации КАК Потребность,
СУММА(Т2.КоличествоПоСпецификации) КАК НарастающийИтог
ПОМЕСТИТЬ ВТ_НарастающийИтог
ИЗ
ВТ_Спецификации КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Спецификации КАК Т2
ПО Т1.Материал = Т2.Материал
И Т1.НомерСтроки >= Т2.НомерСтроки
ГРУППИРОВАТЬ ПО
Т1.Изделие,
Т1.Материал,
Т1.КоличествоПоСпецификации
;
Важное ограничение: данный метод имеет квадратичную сложность. Если в вашей таблице спецификаций тысячи строк, запрос может выполняться крайне долго. Для больших объемов данных всегда старайтесь переходить на использование оконных функций.
После того как мы получили нарастающий итог потребности, нам нужно соединить его с таблицей реальных остатков на складах и применить формулу распределения. Выясним причину, почему это работает. Нам нужно понять, попадает ли наша «строка» в диапазон доступного остатка.
Рассмотрим логическую конструкцию, которую необходимо внедрить в запрос:
ВЫБРАТЬ
Данные.Изделие,
Данные.Материал,
Данные.Потребность,
ВЫБОР
// Весь остаток покрывает нарастающий итог - отдаем всю потребность строки
КОГДА Остатки.КоличествоОстаток >= Данные.НарастающийИтог
ТОГДА Данные.Потребность
// Остатка не хватило даже на предыдущие строки - отдаем 0
КОГДА Остатки.КоличествоОстаток < (Данные.НарастающийИтог - Данные.Потребность)
ТОГДА 0
// Пограничный случай: остатка хватит только на часть этой строки
ИНАЧЕ Остатки.КоличествоОстаток - (Данные.НарастающийИтог - Данные.Потребность)
КОНЕЦ КАК РаспределенныйОстаток
ИЗ
ВТ_НарастающийИтог КАК Данные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Запасы.Остатки КАК Остатки
ПО Данные.Материал = Остатки.Номенклатура
Эта формула позволяет корректно «отрезать» остаток именно на том изделии, где он физически заканчивается. Те изделия, что стоят в конце списка, гарантированно получат 0 в колонку распределения.
Если задача стоит в рамках разработки отчета, мы можем воспользоваться встроенными средствами СКД, которые значительно упрощают получение нарастающих итогов без написания сложных запросов. Разберем по шагам, как это настроить.
В СКД существует функция ВычислитьВыражение. Для поля, где нам нужен нарастающий итог по материалу, в ресурсах можно указать следующее выражение:
ВычислитьВыражение("Сумма(Количество)", , , "Первая", "Текущая")
Преимущества использования СКД:
Мы проанализировали основные способы распределения остатков внутри запроса 1С. Подведем итог: какой метод выбрать в конкретной ситуации?
При реализации любого из этих методов обязательно проверяйте детерминированность сортировки. Если два изделия будут иметь одинаковые веса в упорядочивании, SQL-сервер может вернуть их в разном порядке при разных запусках, что приведет к непредсказуемому распределению остатков. Всегда добавляйте Ссылка или УникальныйИдентификатор в конец секции сортировки.