Как в 1С создать отчет в СКД, который по дням выводит количество постоянных покупателей для каждого продавца?

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

При составлении отчета в 1С, который выводит количество постоянных покупателей по дням в разрезе продавцов, мы сталкиваемся с несколькими ключевыми задачами. Нам необходимо не только отфильтровать постоянных покупателей и правильно их подсчитать, но и обеспечить, чтобы в отчете присутствовали все дни выбранного периода, даже если в какие-то дни не было продаж или визитов от постоянных клиентов. Разберем пошагово, как решить эту задачу, используя возможности системы компоновки данных (СКД) и язык запросов 1С.

Определение "постоянных покупателей"

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

  1. Флаг в справочнике "Контрагенты": Если информация о постоянстве покупателя хранится как булевый реквизит (например, ПостоянныйПокупатель или ДержательКартыЛояльности) непосредственно в справочнике Контрагенты, это самый простой вариант. Мы будем фильтровать покупателей по этому флагу.
  2. Принадлежность к группе контрагентов: Постоянные покупатели могут быть объединены в отдельную группу в справочнике Контрагенты (например, "Постоянные клиенты"). В запросе мы будем проверять вхождение покупателя в эту группу.
  3. Наличие действующей дисконтной карты: Если учет дисконтных карт ведется в отдельном регистре сведений, мы можем соединить данные о продажах с этим регистром и отбирать покупателей, у которых есть активная (действующая) дисконтная карта.
  4. История покупок: Более сложный подход предполагает определение постоянного покупателя по количеству или частоте покупок за определенный период. Для этого потребуется дополнительный подзапрос или временная таблица, анализирующая историю продаж каждого покупателя.

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

Генерация последовательности дат

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

Метод 1: Использование регистра сведений "ДанныеПроизводственногоКалендаря"

Этот метод подходит, если в вашей конфигурации есть такой регистр сведений (обычно он используется в типовых конфигурациях для учета рабочего времени и имеет предзаполненные данные). Рассмотрим, как мы можем использовать его для генерации дат:


ВЫБРАТЬ
    ДанныеПроизводственногоКалендаря.Дата КАК ДатаДня
ИЗ
    РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
ГДЕ
    ДанныеПроизводственногоКалендаря.ПроизводственныйКалендарь = &ПроизводственныйКалендарь
    И ДанныеПроизводственногоКалендаря.Дата МЕЖДУ &ДатаНач И &ДатаКон
УПОРЯДОЧИТЬ ПО
    ДатаДня

Пояснения:

  1. ВЫБРАТЬ ДанныеПроизводственногоКалендаря.Дата КАК ДатаДня: Мы выбираем поле Дата из регистра сведений, переименовывая его для удобства в ДатаДня.
  2. ИЗ РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря: Указываем источник данных — регистр сведений с производственным календарем.
  3. ГДЕ ДанныеПроизводственногоКалендаря.ПроизводственныйКалендарь = &ПроизводственныйКалендарь: Это условие позволяет выбрать конкретный производственный календарь, если их в базе несколько. Параметр &ПроизводственныйКалендарь будет передан из СКД.
  4. И ДанныеПроизводственногоКалендаря.Дата МЕЖДУ &ДатаНач И &ДатаКон: Мы фильтруем даты по заданному диапазону, который пользователь выберет в отчете. Параметры &ДатаНач и &ДатаКон также будут переданы из СКД.
  5. УПОРЯДОЧИТЬ ПО ДатаДня: Обеспечиваем сортировку дат по возрастанию.

Этот запрос создаст временную таблицу со всеми датами в указанном диапазоне, которые присутствуют в производственном календаре.

Метод 2: Генерация последовательности дат с помощью временной таблицы чисел

Если в вашей конфигурации нет подходящего регистра сведений или вам нужен более универсальный способ, можно сгенерировать последовательность дат, используя временную таблицу с числами. Этот метод основывается на многократном самосоединении базовой таблицы из десяти цифр (от 0 до 9) для получения чисел до 100, 1000, 10000 и так далее, а затем использовании этих чисел как приращения к начальной дате периода.

Разберем этот запрос по частям:

Шаг 1: Создание временной таблицы цифр (ВТЦифры)

Сначала нам нужна таблица, содержащая числа от 0 до 9. Мы будем использовать ее для построения более крупных чисел.


ВЫБРАТЬ 0 КАК Цифра
ПОМЕСТИТЬ ВТЦифры
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9
;

Пояснения:

  1. ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ ВТЦифры: Мы начинаем с создания первой записи в нашей временной таблице ВТЦифры, присваивая ей значение 0 и имя поля Цифра.
  2. ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ... ВЫБРАТЬ 9: Далее мы последовательно добавляем числа от 1 до 9, используя оператор ОБЪЕДИНИТЬ ВСЕ. Это создает таблицу, содержащую одну колонку Цифра со значениями от 0 до 9.
Шаг 2: Генерация полной последовательности чисел и преобразование их в даты

Теперь, имея ВТЦифры, мы можем генерировать числа в большом диапазоне, используя несколько ее экземпляров и оператор ЛЕВОЕ СОЕДИНЕНИЕ, а затем преобразовывать эти числа в даты.


////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ,
        Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 +
        ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 +
        ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000), ДЕНЬ) КАК ДатаДня
ПОМЕСТИТЬ ВТДаты
ИЗ
    ВТЦифры КАК Цифры
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + Цифры10.Цифра * 10))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + Цифры10.Цифра * 10 + Цифры100.Цифра * 100))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры1000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + Цифры10.Цифра * 10 + Цифры100.Цифра * 100 + Цифры1000.Цифра * 1000))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + Цифры10.Цифра * 10 + Цифры100.Цифра * 100 + Цифры1000.Цифра * 1000 + Цифры10000.Цифра * 10000))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + Цифры10.Цифра * 10 + Цифры100.Цифра * 100 + Цифры1000.Цифра * 1000 + Цифры10000.Цифра * 10000 + Цифры100000.Цифра * 100000))
ГДЕ
    ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ,
        Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 +
        ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 +
        ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000) <= НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ)
;

////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ ВТЦифры
;

Пояснения:

  1. ВЫБРАТЬ ... ПОМЕСТИТЬ ВТДаты: Мы создаем новую временную таблицу ВТДаты, в которую будем записывать сгенерированные даты.
  2. НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ): Это функция, которая отсекает время от даты, оставляя только начало дня. Мы используем ее для параметров начала и конца периода, чтобы избежать проблем с точным совпадением времени.
  3. ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ, ... ): К начальной дате периода мы последовательно добавляем сгенерированные числа дней.
    • Цифры.Цифра: Это единицы (0-9).
    • ЕСТЬNULL(Цифры10.Цифра, 0) * 10: Это десятки (0-90). Если Цифры10 не соединились (потому что диапазон дат небольшой), то ЕСТЬNULL вернет 0.
    • Аналогично для сотен (* 100), тысяч (* 1000) и так далее, до ста тысяч, что позволяет генерировать даты для очень большого диапазона (до ~270 лет, но обычно достаточно для нескольких лет).
  4. ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10 ПО (...): Здесь происходит самое интересное. Мы многократно соединяем ВТЦифры саму с собой. Условие соединения РАЗНОСТЬДАТ(...) >= (...) гарантирует, что мы генерируем только те числа, которые находятся в пределах нашего заданного диапазона дат между &НачалоПериода и &КонецПериода. Это оптимизирует запрос, не создавая избыточное количество строк.
  5. ГДЕ ДОБАВИТЬКДАТЕ(...) <= НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ): Финальное условие в секции ГДЕ обрезает сгенерированные даты, чтобы они не выходили за пределы &КонецПериода.
  6. УНИЧТОЖИТЬ ВТЦифры: После использования временная таблица ВТЦифры удаляется для освобождения памяти.

В результате мы получаем временную таблицу ВТДаты, которая содержит все дни в выбранном диапазоне от &НачалоПериода до &КонецПериода.

Соединение данных и подсчет покупателей

Теперь, когда у нас есть таблица всех дат, мы можем объединить ее с нашими фактическими данными о продажах или визитах.

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


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

Пояснения:

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

Обратите внимание, что при использовании ЛЕВОЕ СОЕДИНЕНИЕ, если для какой-то даты и продавца не было продаж постоянным покупателям, поля Продавец и Покупатель из таблицы Продажи будут иметь значение NULL. В этом случае КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Покупатель) вернет 0, что нам и нужно для отображения "пустых" дней. Если Продавец также может быть NULL в результате левого соединения, его можно обернуть в ЕСТЬNULL(Продажи.Продавец, "Неизвестный продавец") для более понятного вывода.

Структурирование отчета в СКД

После того как мы составили запрос, нам нужно правильно настроить Систему Компоновки Данных (СКД) для вывода отчета.

  1. Набор данных "Запрос":
    • Создайте новый набор данных типа "Запрос" и вставьте в него вышеуказанный объединенный запрос (сначала генерация ВТЦифры, затем ВТДаты, затем финальный запрос).
    • На вкладке "Параметры" убедитесь, что все параметры (НачалоПериода, КонецПериода, ПроизводственныйКалендарь (если используется), ВыбранныйПродавец) определены. Установите для них типы и значения по умолчанию при необходимости.
    • На вкладке "Поля" убедитесь, что доступны поля ДатаВизита, Продавец и КоличествоПостоянныхПокупателей.
  2. Ресурсы:
    • Перейдите на вкладку "Ресурсы".
    • Для поля КоличествоПостоянныхПокупателей установите выражение Сумма(КоличествоПостоянныхПокупателей). Это позволит СКД автоматически суммировать значения при группировках.
  3. Настройки:
    • Перейдите на вкладку "Настройки" и создайте новую "Настройка по умолчанию".
    • Группировки:
      1. Добавьте группировку по полю Продавец.
      2. Внутри группировки по Продавец добавьте группировку по полю ДатаВизита. Установите для ДатаВизита тип группировки "День".
      3. Если вы хотите видеть промежуточные итоги за месяц, вы можете добавить еще одну группировку между Продавец и ДатаВизита, например, по выражению НАЧАЛОПЕРИОДА(ДатаВизита, МЕСЯЦ) с именем "Месяц".
    • Выбранные поля:
      • Добавьте в выбранные поля Продавец, ДатаВизита и КоличествоПостоянныхПокупателей.
    • Параметры:
      • На вкладке "Параметры" настройте параметры НачалоПериода и КонецПериода так, чтобы они были доступны для пользователя.
      • Также настройте параметр ВыбранныйПродавец, если предусмотрен отбор по конкретному продавцу.

Таким образом, мы получим отчет, который будет выводить для каждого продавца список дней в выбранном диапазоне, а напротив каждого дня — количество постоянных покупателей, которые его посетили. Итоги будут автоматически рассчитаны СКД для группировок (например, по продавцу и по месяцу, если вы добавили такую группировку).

Пример итогового запроса (с генерацией дат через числа)

Представим полный запрос, который вы могли бы вставить в набор данных СКД:


// Временная таблица цифр от 0 до 9
ВЫБРАТЬ 0 КАК Цифра
ПОМЕСТИТЬ ВТЦифры
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9
;

////////////////////////////////////////////////////////////////////////////////
// Временная таблица всех дней в заданном периоде
ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ,
        Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 +
        ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 +
        ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000) КАК ДатаДня
ПОМЕСТИТЬ ВТДаты
ИЗ
    ВТЦифры КАК Цифры
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры1000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 + ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 + ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000))
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100000
        ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ), ДЕНЬ) >= (Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 + ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 + ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000))
ГДЕ
    ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ,
        Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 +
        ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 +
        ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000) <= НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ)
;

////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ ВТЦифры
;

////////////////////////////////////////////////////////////////////////////////
// Основной запрос, объединяющий даты с данными о продажах постоянным покупателям
ВЫБРАТЬ
    ВТДаты.ДатаДня КАК ДатаВизита,
    Продажи.Продавец КАК Продавец,
    ЕСТЬNULL(КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Покупатель), 0) КАК КоличествоПостоянныхПокупателей
ИЗ
    ВТДаты КАК ВТДаты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВашиПродажи КАК Продажи
        ПО ВТДаты.ДатаДня = НАЧАЛОПЕРИОДА(Продажи.Дата, ДЕНЬ)
ГДЕ
    (Продажи.Покупатель ССЫЛКА Справочник.Контрагенты
     И Продажи.Покупатель.ПостоянныйПокупатель = ИСТИНА)
    И (Продажи.Продавец = &ВыбранныйПродавец ИЛИ &ВыбранныйПродавец ЕСТЬ NULL)
    И ВТДаты.ДатаДня МЕЖДУ НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ) И НАЧАЛОПЕРИОДА(&КонецПериода, ДЕНЬ)
СГРУППИРОВАТЬ ПО
    ВТДаты.ДатаДня,
    Продажи.Продавец
УПОРЯДОЧИТЬ ПО
    Продажи.Продавец,
    ДатаВизита

В этом запросе мы добавили ЕСТЬNULL(КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Покупатель), 0), чтобы явно заменить NULL на 0 в поле количества покупателей для тех дней, когда не было данных. Это делает вывод отчета более аккуратным и понятным.

Заключение

Мы рассмотрели детальный подход к созданию отчета в 1С СКД, который позволяет подсчитывать постоянных покупателей по дням в разрезе продавцов, при этом отображая все дни выбранного периода. Ключевыми моментами являются:

  1. Правильное определение критерия "постоянный покупатель" и его отражение в запросе.
  2. Генерация полной последовательности дат за выбранный период с использованием регистра производственного календаря или временной таблицы чисел.
  3. Использование ЛЕВОЕ СОЕДИНЕНИЕ для объединения сгенерированных дат с данными продаж, чтобы включить в отчет дни без активности.
  4. Применение функций КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) для подсчета уникальных покупателей и ЕСТЬNULL(...) для корректного отображения нулей.
  5. Настройка группировок и ресурсов в СКД для получения желаемого вида отчета с поденной детализацией и итоговыми значениями.

Этот подход обеспечивает гибкость и точность при анализе данных о покупателях и работе продавцов, позволяя видеть полную картину за любой выбранный период.

← На главную