Как в отчете 1С разделить продажи на первичные и вторичные покупки

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

При анализе эффективности маркетинга, рекламных кампаний и поведения клиентов (например, в фитнес-клубах или розничной торговле) часто возникает задача классифицировать продажи на первичные (первый визит или первая покупка контрагента) и вторичные (все последующие заказы). Выясним причину сложности этой задачи: если в базе данных накоплена многолетняя история, вычисление самого первого документа «на лету» может существенно замедлять формирование отчетов. Разберем по шагам основные архитектурные подходы к решению проблемы.

Способ 1. Получение первой покупки запросом через агрегатные функции

Проанализируем классический подход без изменения структуры метаданных конфигурации. В этом случае мы получаем минимальную дату продажи по каждому контрагенту с помощью функции МИНИМУМ() и связываем этот результат с общим списком документов или оборотов.

Посмотрим на пример запроса, который определяет первый документ реализации для каждого клиента:


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

Обратим внимание на важный нюанс: если в одну секунду по контрагенту проведено несколько реализаций, простое соединение по дате вызовет дублирование строк. Чтобы исключить неоднозначность, во внешней группировке мы дополнительно используем агрегатную функцию МИНИМУМ(Продажи.Ссылка).

Затем соединяем полученную временную таблицу с основной выборкой за требуемый период отчета:


ВЫБРАТЬ
    ТекущиеПродажи.Контрагент КАК Контрагент,
    ТекущиеПродажи.Ссылка КАК ДокументПродажи,
    ВЫБОР
        КОГДА ВТ_ПервыеДокументы.ПервыйДокумент ЕСТЬ НЕ NULL ТОГДА "Первичная"
        ИНАЧЕ "Вторичная"
    КОНЕЦ КАК ТипПокупки
ИЗ
    Документ.РеализацияТоваровУслуг КАК ТекущиеПродажи
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПервыеДокументы КАК ВТ_ПервыеДокументы
        ПО ТекущиеПродажи.Контрагент = ВТ_ПервыеДокументы.Контрагент
        И ТекущиеПродажи.Ссылка = ВТ_ПервыеДокументы.ПервыйДокумент
ГДЕ
    ТекущиеПродажи.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И ТекущиеПродажи.Проведен = ИСТИНА;

Способ 2. Двухэтапная фильтрация для ускорения отчета за период

Рассмотрим подробнее ситуацию, когда база данных содержит миллионы записей за несколько лет. Поиск минимальной даты по всей таблице документов приведет к полному сканированию индексов и длительному ожиданию. Чтобы оптимизировать построение отчета, разделим алгоритм на шаги:

  1. Отбираем во временную таблицу только тех контрагентов, у которых были продажи в указанном пользователем периоде отчета.
  2. Проверяем наличие движений по этим отобранным клиентам строго до начала периода отчета (Дата < &НачалоПериода).
  3. Анализируем результат: если у клиента не было движений до начала периода, то его самая первая покупка внутри периода гарантированно является первичной для всей базы. Если движения до периода были — все продажи в текущем интервале являются вторичными.

Такой подход избавляет СУБД от необходимости вычислять абсолютный минимум по контрагентам, совершавшим покупки годами ранее.

Способ 3. Хранение признака первой покупки в данных

Если отчет по новым клиентам формируется регулярно руководящим составом, оптимальным решением является предварительный расчет и сохранение данных, чтобы не нагружать систему тяжелыми запросами.

  1. Реквизит в справочнике: Добавим в справочник Контрагенты реквизит ПерваяПродажа с типом ссылки на документ реализации или датой. При проведении документа продажи проверяем: если реквизит еще не заполнен, записываем текущую реализацию.
  2. Непериодический регистр сведений: Создадим регистр сведений ПервыеПродажиКлиентов с измерением Контрагент и ресурсами Документ и ДатаПервойПродажи. Заполнение выполняем в подписке на событие ОбработкаПроведения документов реализации.

Важные особенности при хранении данных в реквизитах и регистрах:

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

← На главную