Как вывести регистратор в отчете по регистру накопления, если установлена периодичность День?

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

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

Почему нельзя просто выбрать Регистратор при периодичности День?

Для начала проанализируем ситуацию. Виртуальная таблица ОстаткиИОбороты с периодичностью День выполняет предварительную группировку записей на уровне СУБД. Если за 10 октября было 50 документов РеализацияТоваровУслуг, запрос вернет одну строку за эту дату. Платформа не «знает», какой именно из 50 регистраторов вам нужно показать в этой единственной строке. Поэтому поле Регистратор исключается из состава доступных полей. Чтобы обойти это ограничение, нам придется либо изменить подход к архитектуре запроса, либо использовать соединение с физической таблицей регистра.

Решение 1. Использование временных таблиц и соединения с физической таблицей

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


// Шаг 1: Получаем обороты в разрезе дней
ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(ТоварыНаСкладахОстаткиИОбороты.Период, ДЕНЬ) КАК День,
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоОборот
ПОМЕСТИТЬ втПоДню
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ТоварыНаСкладахОстаткиИОбороты
;

// Шаг 2: Находим какой-либо регистратор (например, максимальный) за этот день
ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ) КАК День,
    ТоварыНаСкладах.Номенклатура,
    МАКСИМУМ(ТоварыНаСкладах.Регистратор) КАК Регистратор
ПОМЕСТИТЬ втМаксРегПоДню
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
    (ТоварыНаСкладах.Номенклатура, НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ)) В
        (ВЫБРАТЬ
            втПоДню.Номенклатура,
            втПоДню.День
        ИЗ
            втПоДню КАК втПоДню)
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура,
    НАЧАЛОПЕРИОДА(ТоварыНаСкладах.Период, ДЕНЬ)
;

// Шаг 3: Соединяем данные
ВЫБРАТЬ
    втПоДню.День,
    втПоДню.Номенклатура,
    ЕСТЬNULL(втМаксРегПоДню.Регистратор, "Нет документа") КАК Регистратор,
    втПоДню.КоличествоОборот
ИЗ
    втПоДню КАК втПоДню
        ЛЕВОЕ СОЕДИНЕНИЕ втМаксРегПоДню КАК втМаксРегПоДню
        ПО втПоДню.Номенклатура = втМаксРегПоДню.Номенклатура
            И втПоДню.День = втМаксРегПоДню.День

Проанализируем этот алгоритм. В первой временной таблице втПоДню мы получаем чистые данные из виртуальной таблицы. Это работает быстро. Во второй таблице втМаксРегПоДню мы обращаемся к физической таблице РегистрНакопления.ТоварыНаСкладах. Использование функции МАКСИМУМ позволяет нам схлопнуть все документы дня в одну строку, чтобы количество строк совпало с первой таблицей. Однако помните: это решение ресурсоемкое, так как чтение физической таблицы движений при больших объемах данных замедляет отчет. Для оптимизации работы с памятью в сложных пакетах запросов можно использовать обработку для анализа и расстановки команд уничтожения временных таблиц.

Решение 2. Смена периодичности на «Авто» (Оптимальный способ)

Выясним причину, по которой многие избегают периодичности Регистратор. Обычно это страх получить слишком детальную выборку, которую сложно обрабатывать. Но в 1С есть механизм, который называется периодичность Авто. Рассмотрим, как это работает в запросе:

  1. Если мы устанавливаем в параметрах виртуальной таблицы периодичность Авто, нам становятся доступны поля: ПериодСекунда, ПериодДень, ПериодМесяц и Регистратор.
  2. Если в списке полей ВЫБРАТЬ мы укажем и ПериодДень, и Регистратор, система вернет детальные записи.
  3. Если же отчет строится в Системе Компоновки Данных (СКД), мы можем гибко управлять группировками.

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

Решение 3. Обработка ситуации с несколькими документами в день

Бывают задачи, когда нужно не просто вывести «какой-нибудь» регистратор, а распределить дневной оборот между всеми документами дня. Рассмотрим сложный пример, где мы считаем количество документов за день и пытаемся сопоставить их с общим итогом.


ВЫБРАТЬ
    втМаксРегПоДню.День,
    втМаксРегПоДню.Номенклатура,
    СУММА(1) КАК КоличествоДокументовЗаДень
ПОМЕСТИТЬ втДокументовВДень
ИЗ
    втМаксРегПоДню КАК втМаксРегПоДню
СГРУППИРОВАТЬ ПО
    втМаксРегПоДню.День,
    втМаксРегПоДню.Номенклатура
;

ВЫБРАТЬ
    втПоДню.День,
    втПоДню.Номенклатура,
    втМаксРегПоДню.Регистратор,
    втПоДню.КоличествоОборот / ЕСТЬNULL(втДокументовВДень.КоличествоДокументовЗаДень, 1) КАК РаспределенныйОборот
ИЗ
    втПоДню КАК втПоДню
        ЛЕВОЕ СОЕДИНЕНИЕ втМаксРегПоДню КАК втМаксРегПоДню
        ПО втПоДню.Номенклатура = втМаксРегПоДню.Номенклатура
            И втПоДню.День = втМаксРегПоДню.День

Этот подход (предложенный в обсуждении) довольно специфичен. Он может пригодиться, если вам нужно визуально «размазать» итоги дня по строкам с регистраторами. Однако на практике чаще всего достаточно использовать правильную настройку ролей полей в СКД (поле Регистратор — роль «Период, 1», поле ПериодДень — роль «Период, 2»).

Важные рекомендации по производительности

Проанализируем основные ошибки, которые могут привести к зависанию отчета при попытке вытащить регистратор в «дневной» отчет:

Подведем итог: Самый простой путь — использовать СКД с периодичностью Авто или Регистратор и настроить группировку по дням в интерфейсе настроек. Если же вы ограничены чистым запросом и вам нужен именно один документ в строке дня — используйте метод с временной таблицей и агрегатной функцией МАКСИМУМ(Регистратор).

← На главную