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

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

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

Анализ типичной ошибки при получении остатков

Рассмотрим ситуацию, когда разработчик создает две временные таблицы, выбирая данные из РегистрБухгалтерии.Хозрасчетный.Остатки. Основная проблема здесь заключается в способе объединения этих данных. Если просто перечислить таблицы в секции ИЗ через запятую, система выполнит декартово произведение. Это означает, что каждая строка из первой таблицы будет сопоставлена с каждой строкой из второй. В итоге мы получим огромное количество дублирующихся и неверных данных.

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


ВЫБРАТЬ
    ХозрасчетныйОстатки.Субконто1 КАК Контрагент,
    ХозрасчетныйОстатки.СуммаОстатокДт КАК СуммаНачало
ПОМЕСТИТЬ ВТ_Начало
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Остатки(&ДатаНачала, Счет = &Счет, , Субконто1 = &Контрагент) КАК ХозрасчетныйОстатки;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ХозрасчетныйОстатки.Субконто1 КАК Контрагент,
    ХозрасчетныйОстатки.СуммаОстатокДт КАК СуммаКонец
ПОМЕСТИТЬ ВТ_Конец
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Остатки(&ДатаКонца, Счет = &Счет, , Субконто1 = &Контрагент) КАК ХозрасчетныйОстатки;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Начало.Контрагент,
    ВТ_Начало.СуммаНачало,
    ВТ_Конец.СуммаКонец
ИЗ
    ВТ_Начало КАК ВТ_Начало,
    ВТ_Конец КАК ВТ_Конец

В данном примере отсутствует условие связи (ПО или ГДЕ). Даже если добавить связь по полю Контрагент, использование двух отдельных обращений к виртуальным таблицам остатков не является оптимальным с точки зрения производительности СУБД.

Оптимальное решение: Использование таблицы ОстаткиИОбороты

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

Рассмотрим преимущества этого метода:

  1. Минимизация обращений: Один запрос к базе данных вместо двух или более.
  2. Готовые поля: В таблице уже присутствуют поля СуммаНачальныйОстатокДт, СуммаКонечныйОстатокДт и аналогичные для кредитового остатка.
  3. Автоматическая группировка: Платформа сама заботится о корректном сопоставлении данных по измерениям и субконто.

Разберем пример построения такого запроса:


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

Важное замечание по параметрам: В виртуальной таблице ОстаткиИОбороты крайне важно правильно указывать периодичность. Если вам нужны общие итоги за весь период, этот параметр можно оставить пустым или указать Период.

Работа с границами периода

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

Для решения этой проблемы выясним причину и применим объект Граница. Выполним следующие шаги в программном коде перед вызовом запроса:


КонецПериодаГраница = Новый Граница(КонецДня(ДатаКонца), ВидГраницы.Включая);
Запрос.УстановитьПараметр("КонецПериода", КонецПериодаГраница);

Использование ВидГраницы.Включая гарантирует, что все движения, совершенные в последнюю секунду указанного периода, будут учтены в конечном остатке.

Нюансы при соединении таблиц (если ОстаткиИОбороты не подходят)

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

  1. Используйте ПОЛНОЕ СОЕДИНЕНИЕ: Если контрагент имел остаток на начало периода, но к концу периода он стал нулевым, или наоборот — остаток появился только в конце, обычное ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ может привести к потере данных.
  2. Применяйте функцию ЕСТЬNULL: При соединении таблиц для числовых полей всегда используйте ЕСТЬNULL(Сумма, 0), чтобы избежать пустых значений в результате, которые могут сломать дальнейшие расчеты.
  3. Выбирайте ключевые поля через ЕСТЬNULL: Поле Контрагент также нужно выбирать из обеих таблиц, используя функцию ЕСТЬNULL(ВТ_Начало.Контрагент, ВТ_Конец.Контрагент).

Посмотрим на пример правильно настроенного соединения двух временных таблиц:


ВЫБРАТЬ
    ЕСТЬNULL(ВТ_Начало.Контрагент, ВТ_Конец.Контрагент) КАК Контрагент,
    ЕСТЬNULL(ВТ_Начало.СуммаНачало, 0) КАК СуммаНачало,
    ЕСТЬNULL(ВТ_Конец.СуммаКонец, 0) КАК СуммаКонец
ИЗ
    ВТ_Начало КАК ВТ_Начало
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Конец КАК ВТ_Конец
        ПО ВТ_Начало.Контрагент = ВТ_Конец.Контрагент

Проверка условий в параметрах виртуальных таблиц

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

Если написать условие в секции ГДЕ, система сначала получит абсолютно все остатки по всем счетам и контрагентам в память, а затем начнет их фильтровать. Это приведет к колоссальной нагрузке на сервер. Всегда указывайте Счет = &Счет внутри параметров виртуальной таблицы, как показано в примерах выше. Это позволяет СУБД использовать индексы и возвращать только нужные строки.

Подводя итог, можно сказать: для большинства задач получения данных "на начало" и "на конец" идеальным инструментом является таблица ОстаткиИОбороты в сочетании с объектом Граница для корректной обработки времени. Только в исключительных случаях стоит прибегать к ручному соединению нескольких таблиц остатков, соблюдая правила использования ПОЛНОЕ СОЕДИНЕНИЕ и функции ЕСТЬNULL.

← На главную