Как получить поле Период в запросе к виртуальной таблице оборотов регистра накопления?

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

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

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

Суть проблемы и анализ ошибки

Рассмотрим ситуацию, с которой столкнулся автор вопроса. Имеется конфигурация (в данном случае «Управление торговлей 11.5») и создается запрос в расширении. Цель — получить данные из регистра накопления ТоварыНаСкладах.

Разработчик пишет следующий запрос:


ВЫБРАТЬ
    ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
    ТоварыНаСкладахОбороты.Период КАК Период
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты КАК ТоварыНаСкладахОбороты

При выполнении этого кода платформа выдает ошибку:

Поле не найдено "ТоварыНаСкладахОбороты.Период"

Первая мысль, которая может возникнуть: проблема в расширении конфигурации или в специфике платформы 8.3.24. Однако причина кроется в фундаментальных принципах работы языка запросов 1С.

Физические таблицы против Виртуальных таблиц

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

  1. Физическая таблица (РегистрНакопления.ТоварыНаСкладах) — это таблица, где реально хранятся записи движений (проводки). У каждой записи всегда есть стандартные реквизиты: Период, Регистратор, НомерСтроки и т.д. Если мы делаем запрос к этой таблице, поле Период доступно всегда.
  2. Виртуальная таблица (РегистрНакопления.ТоварыНаСкладах.Обороты) — это не просто выборка записей. Это интеллектуальный механизм, который агрегирует (сворачивает) данные. По умолчанию, если мы не просим обратного, система суммирует все движения за указанный интервал времени и выдает одну строку с общими итогами по измерениям (например, по Номенклатуре и Складу).

В свернутом итоге понятия «Период» не существует, так как сумма может состоять из движений за 1-е, 5-е и 30-е число месяца. Именно поэтому поле Период отсутствует в структуре полей выборки по умолчанию.

Решение: Параметр периодичности

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

Синтаксис виртуальной таблицы оборотов выглядит так:

РегистрНакопления.ИмяРегистра.Обороты(&НачалоПериода, &КонецПериода, <Периодичность>, <Условие>)

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


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

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

Варианты настройки периодичности

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

В языке запросов 1С допустимы следующие значения периодичности:

  1. Период. Это стандартное значение. Поле результата будет называться Период. Группировка происходит по минимальной периодичности, необходимой для разделения записей, но на практике чаще используются конкретные интервалы.
  2. Временные интервалы:
    • Секунда
    • Минута
    • Час
    • День
    • Неделя
    • Декада
    • Месяц
    • Квартал
    • Полугодие
    • Год
    При выборе любого из этих вариантов в выборке появляется поле Период. Значением поля будет начало соответствующего интервала (например, начало дня или начало месяца).
  3. Запись (или Регистратор). Это особый режим. В этом случае система не просто группирует данные по времени, она включает в выборку документ, сделавший движение.
    В результате появляются поля:
    • Период (с точностью до секунды/момента времени)
    • Регистратор (ссылка на документ)
    • НомерСтроки
    Это полезно, если вам нужны обороты в разрезе конкретных документов.
  4. Авто. Система использует периодичность, заданную в свойствах самого регистра накопления в конфигураторе. При этом поле в выборке может называться, например, ПериодСекунда, если основной период регистра — секунда. В коде это может выглядеть так:
    
    ВЫБРАТЬ
       Таблица.ПериодСекунда КАК ПериодСекунда
    ИЗ
       РегистрНакопления.ТоварыНаСкладах.Обороты(,,, Авто) КАК Таблица
    

Сравнение правильного и неправильного подходов

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

Вариант 1: Запрос к физической таблице (Период есть всегда)


ВЫБРАТЬ
    т1.Номенклатура КАК Номенклатура,
    т1.Период КАК Период -- Это поле существует физически в таблице
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК т1
ГДЕ
    т1.Период МЕЖДУ &Дата1 И &Дата2

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

Вариант 2: Запрос к виртуальной таблице (Период добавляется параметром)


ВЫБРАТЬ
   ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
   ТоварыНаСкладахОбороты.Период КАК Период -- Поле появится благодаря параметру "Месяц"
ИЗ
   РегистрНакопления.ТоварыНаСкладах.Обороты(&Дата1, &Дата2, Месяц, ) КАК ТоварыНаСкладахОбороты

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

Оптимизация и важные замечания

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

1. Избегайте избыточного разыменования ссылок

В примере кода автора встречалась конструкция:

ЗаказыНаСборкуОбороты.ЗаказНаСборку.Ссылка

Поле ЗаказНаСборку (если это измерение регистра) уже само по себе является ссылкой на документ. Дописывать .Ссылка в конце не нужно. Это заставляет SQL-сервер делать лишнее (хоть и оптимизируемое современными платформами) действие. Правильно писать просто:

ЗаказыНаСборкуОбороты.ЗаказНаСборку

2. Правильный выбор таблицы (Остатки или Обороты)

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

3. Влияние периодичности на производительность

Выбор периодичности — это не только вопрос отображения, но и вопрос скорости.

Поэтому, если вам нужны общие обороты за период без детализации по дням, не указывайте мелкую периодичность (день, секунда) и тем более периодичность "Запись".

Итоговый пример кода

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


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

// Обязательно устанавливаем параметры дат
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата())); 
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));

Результат = Запрос.Выполнить();

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

← На главную