В типовых конфигурациях, таких как 1С:Управление торговлей 11, подсистема планирования не всегда удовлетворяет специфическим требованиям бизнеса. Часто возникает задача создать собственную систему планирования, где планы ставятся не просто на конкретную позицию, а на группы, сегменты или произвольные наборы номенклатуры и партнеров. Основная сложность здесь заключается в построении отчета «План-факт», который должен сопоставить общие цифры плана с детальными данными фактических продаж. Рассмотрим, как решить эту задачу эффективно.
Прежде чем приступать к написанию запроса, проанализируем структуру данных. Для гибкого планирования нам недостаточно стандартного регистра. Мы будем использовать связку из трех таблиц (регистров сведений или накопления):
КлючСвязи.КлючСвязи и ссылку на Номенклатуру (или группу/сегмент).КлючСвязи и ссылку на Партнера.Такая структура позволяет одной строке плана (например, «Продать 100 единиц товара») соответствовать списку из 50 конкретных товаров и 10 конкретных клиентов. Наша задача — «подтянуть» факт продаж именно по этим соответствиям.
Проанализируем ситуацию, когда количество планов невелико. Можно пойти по пути формирования текста запроса в цикле. Мы выбираем все установленные планы, а затем для каждого плана генерируем подзапрос к регистру продаж с соответствующими фильтрами. Рассмотрим пример кода, реализующий такую логику:
// Фрагмент формирования запроса в цикле
МассивЗапросов = Новый Массив;
Пока ВыборкаПланов.Следующий() Цикл
ТекстПодзапроса = "ВЫБРАТЬ &КлючСвязи КАК КлючСвязи, СУММА(КоличествоОборот) КАК Факт
ИЗ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(...)
ГДЕ АналитикаНоменклатуры.Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ОтборПоНоменклатуре ГДЕ КлючСвязи = &КлючСвязи)";
// Подстановка параметров и добавление в массив
МассивЗапросов.Добавить(ТекстПодзапроса);
КонецЦикла;
ПолныйТекстЗапроса = СтрСоединить(МассивЗапросов, " ОБЪЕДИНИТЬ ВСЕ ");
Важный момент: этот подход прост в реализации «в лоб», но крайне непроизводителен при большом количестве строк в документе плана. Каждое ОБЪЕДИНИТЬ ВСЕ заставляет СУБД выполнять отдельное сканирование таблиц продаж. Рассмотрим более элегантный метод.
Выясним, как собрать все данные одним пакетом запросов. Это наиболее правильный путь с точки зрения производительности 1С. Разберем процесс по шагам.
Шаг 1. Подготовка данных о продажах. Сначала мы собираем все продажи за интересующий нас период во временную таблицу. Мы не накладываем отборы по номенклатуре здесь, чтобы не ограничивать выборку заранее.
ВЫБРАТЬ
Продажи.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
Продажи.АналитикаУчетаПоПартнерам.Партнер КАК Партнер,
Продажи.КоличествоОборот КАК Количество,
Продажи.СуммаВыручкиОборот КАК Выручка,
Продажи.ЗаказКлиента КАК ЗаказКлиента
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &ОкончаниеПериода, , Менеджер = &Менеджер) КАК Продажи;
Шаг 2. Подготовка таблицы планов. Выбираем планы менеджера в другую временную таблицу ВТ_Планы.
Шаг 3. Соединение планов и факта по условиям. Проанализируем самую сложную часть — сопоставление. Мы должны соединить таблицу планов с таблицей продаж так, чтобы продажа попала в план, только если она удовлетворяет отборам по номенклатуре и партнеру (если они заданы).
ВЫБРАТЬ
ПланПродаж.ТипПлана,
ПланПродаж.Значение КАК План,
ВЫБОР
КОГДА ПланПродаж.ТипПлана = ЗНАЧЕНИЕ(Перечисление.ТипыПланов.Количество) ТОГДА СУММА(ВТ_Продажи.Количество)
КОГДА ПланПродаж.ТипПлана = ЗНАЧЕНИЕ(Перечисление.ТипыПланов.Выручка) ТОГДА СУММА(ВТ_Продажи.Выручка)
КОГДА ПланПродаж.ТипПлана = ЗНАЧЕНИЕ(Перечисление.ТипыПланов.КоличествоSKU) ТОГДА КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Продажи.Номенклатура)
КОНЕЦ КАК Факт,
ВТ_Продажи.Номенклатура,
ВТ_Продажи.Партнер
ИЗ
ВТ_Планы КАК ПланПродаж
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
ПО (ВЫБОР
КОГДА ПланПродаж.ЕстьОтборПоНоменклатуре
ТОГДА ВТ_Продажи.Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ РегОтборыНоменклатура ГДЕ КлючСвязи = ПланПродаж.КлючСвязи)
ИНАЧЕ ИСТИНА
КОНЕЦ)
И (ВЫБОР
КОГДА ПланПродаж.ЕстьОтборПоПартнерам
ТОГДА ВТ_Продажи.Партнер В (ВЫБРАТЬ Партнер ИЗ РегОтборыПартнеры ГДЕ КлючСвязи = ПланПродаж.КлючСвязи)
ИНАЧЕ ИСТИНА
КОНЕЦ)
СГРУППИРОВАТЬ ПО
ПланПродаж.ТипПлана,
ПланПродаж.Значение,
ВТ_Продажи.Номенклатура,
ВТ_Продажи.Партнер;
Внимание: Использование подзапросов внутри ВЫБОР в условии ПО может быть запрещено платформой или работать крайне медленно. Оптимальнее сделать несколько ЛЕВЫХ СОЕДИНЕНИЙ с таблицами отборов и проверять наличие записи через ЕСТЬNULL.
Рассмотрим подробнее расчет показателей «Вес» и «Количество SKU».
ВЫРАЗИТЬ для численных полей пересчета и соединять данные с таблицей единиц измерения. Будьте внимательны с нулевыми знаменателями — всегда используйте ВЫБОР КОГДА Знаменатель <> 0.КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура). Однако помните, что при группировке по партнерам общая сумма уникальных SKU не будет равна сумме SKU по каждому партнеру (один товар может быть куплен разными клиентами).Проанализируем ситуацию: если отчет должен быть гибким (с пользовательскими настройками группировок), лучше всего использовать Схему компоновки данных (СКД). Мы можем создать два набора данных — «НаборДанныхПлан» и «НаборДанныхФакт». В СКД на закладке «Связи наборов данных» мы настроим соединение по периоду и менеджеру, а параметры отборов (номенклатуру и партнеров) передадим из плана в факт в качестве параметров связи.
Это позволит:
JOIN в одном запросе.Таким образом, комбинируя архитектуру с ключевыми идентификаторами связей и мощные инструменты запросов 1С (временные таблицы и СКД), мы получаем гибкую и производительную систему план-фактного анализа, готовую к любым изменениям требований бизнеса.