Как в 1С создать запрос для поиска товаров, которые клиент не покупал за период

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

В практике работы программиста 1С часто возникает задача анализа «упущенных возможностей». Одной из таких задач является формирование отчета по товарам, которые клиент не приобрел за определенный период времени, хотя эти товары были в наличии или входят в регулярный ассортимент. Это позволяет отделу продаж предлагать клиенту новые позиции и расширять его «корзину».

В данной статье мы подробно разберем, как реализовать такой отчет на языке запросов 1С, особенно для конфигураций на обычных формах, таких как «Управление торговлей 10.3», и рассмотрим нюансы оптимизации производительности при работе с большими объемами данных.

Постановка задачи и логика решения

Рассмотрим ситуацию: у нас есть полный список товаров (например, 100 наименований на складе) и есть данные о продажах конкретному контрагенту (например, он купил только 20 наименований). Наша цель — получить список из оставшихся 80 товаров.

С точки зрения теории множеств, нам нужно найти разность между множеством всех доступных товаров и множеством товаров, купленных клиентом. В языке запросов 1С это реализуется через механизм ЛЕВОЕ СОЕДИНЕНИЕ двух таблиц по полю Номенклатура с последующей фильтрацией тех записей, для которых не нашлось соответствия в правой таблице.

Шаг 1: Определение источника «всех товаров»

Прежде всего, нам нужно решить, что мы считаем «всеми товарами». Рассмотрим три варианта:

  1. Весь справочник Номенклатура: включает в себя всё, что когда-либо было заведено в базу.
  2. Товары в наличии: только то, что реально лежит на складе и что клиент мог бы купить.
  3. Обороты продаж по всем клиентам: товары, которые в принципе продавались в этот период кому угодно.

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ВсеТовары
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков, ) КАК ТоварыНаСкладахОстатки
ГДЕ
    ТоварыНаСкладахОстатки.КоличествоОстаток > 0
;

Шаг 2: Получение данных о покупках клиента

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПродажиОбороты.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_КупленныеТовары
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , Контрагент = &ВыбранныйКлиент) КАК ПродажиОбороты
;

Шаг 3: Соединение таблиц и поиск «пустот»

Это самый важный этап. Мы берем таблицу ВТ_ВсеТовары и присоединяем к ней ВТ_КупленныеТовары. Те товары, которые клиент не покупал, будут иметь значение NULL в колонках правой таблицы.

Разберем готовый код запроса:


ВЫБРАТЬ
    ВсеТовары.Номенклатура КАК ТоварКоторыйНеКупили
ИЗ
    ВТ_ВсеТовары КАК ВсеТовары
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КупленныеТовары КАК КупленныеТовары
        ПО ВсеТовары.Номенклатура = КупленныеТовары.Номенклатура
ГДЕ
    КупленныеТовары.Номенклатура ЕСТЬ NULL

Почему это работает? Условие ЕСТЬ NULL отсекает все строки, где произошло успешное соединение (то есть те товары, которые клиент купил). Остаются только те позиции из левой таблицы, для которых в правой таблице (продажах) записей не обнаружено.

Решение для нескольких клиентов (Декартово произведение)

Если задача усложняется и нам нужно построить матрицу по всем клиентам (какой клиент что не купил), обычного левого соединения будет недостаточно. Нам понадобится создать таблицу всех возможных комбинаций пар «Клиент — Номенклатура».

Рассмотрим, как это реализовать:

  1. Создаем таблицу всех активных клиентов.
  2. Создаем таблицу всех актуальных товаров.
  3. Соединяем их без условия (Cross Join), чтобы получить полную матрицу.
  4. Левым соединением накладываем реальные продажи.

ВЫБРАТЬ
    ВыбранныеКонтрагенты.Ссылка КАК Контрагент,
    ВыбраннаяНоменклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Матрица
ИЗ
    Справочник.Контрагенты КАК ВыбранныеКонтрагенты
        СОЕДИНЕНИЕ Справочник.Номенклатура КАК ВыбраннаяНоменклатура
        ПО (ИСТИНА) // Перекрестное соединение
ГДЕ
    НЕ ВыбранныеКонтрагенты.ЭтоГруппа
    И НЕ ВыбраннаяНоменклатура.ЭтоГруппа;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Матрица.Контрагент,
    Матрица.Номенклатура
ИЗ
    ВТ_Матрица КАК Матрица
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты(&Нач, &Кон, , ) КАК Продажи
        ПО Матрица.Контрагент = Продажи.Контрагент
            И Матрица.Номенклатура = Продажи.Номенклатура
ГДЕ
    Продажи.Номенклатура ЕСТЬ NULL

Важное замечание по производительности: Будьте осторожны с перекрестным соединением. Если в базе 1 000 клиентов и 10 000 товаров, временная таблица составит 10 000 000 записей. Всегда используйте жесткие фильтры по группам номенклатуры или конкретным сегментам клиентов.

Реализация через Систему компоновки данных (СКД)

В современных конфигурациях (да и в УТ 10.3 при использовании СКД) такие отчеты можно строить еще элегантнее. Вы можете создать два набора данных: один с полным списком товаров, другой — с продажами. В настройках связей наборов данных можно указать «Левое соединение» и в настройках самого отчета вывести только те записи, где количество из набора продаж НЕ ЗАПОЛНЕНО.

Проанализируем преимущества использования Временных таблиц в таких задачах:

Практические советы

При построении подобных отчетов в «1С:Управление торговлей 10.3», обратите внимание на следующие моменты:

  1. Учет характеристик: Если вы ведете учет в разрезе характеристик, соединение должно идти по паре полей Номенклатура и ХарактеристикаНоменклатуры.
  2. Пометка удаления: Обязательно исключайте из выборки товары и контрагентов, помеченных на удаление (ПометкаУдаления = ЛОЖЬ).
  3. Типы номенклатуры: Скорее всего, вам не нужно анализировать услуги или тару. Добавьте фильтр ВидНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ВидыНоменклатуры.Товар).

Таким образом, мы выяснили, что ключ к решению задачи — правильное использование ЛЕВОГО СОЕДИНЕНИЯ с фильтрацией по NULL. Этот метод универсален и подходит для поиска любых «отсутствующих» данных в системе.

← На главную