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