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

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

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

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

В чем заключается проблема «выпадающих» периодов?

Рассмотрим стандартное обращение к виртуальной таблице РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты. Если мы укажем периодичность Месяц, то СУБД вернет строки только за те периоды, в которых были зафиксированы реальные хозяйственные операции (обороты) по выбранным счетам.

Представим ситуацию: на начало года у нас числится дебетовый остаток по счету 60.01 в размере 50 000 рублей. В январе, феврале и марте никаких движений по этому контрагенту не происходило. Если мы сделаем обычный запрос за первый квартал с периодичностью по месяцам, то система вернет нам пустой результат для февраля и марта, либо вообще проигнорирует эти месяцы в выборке. Для построения красивого и достоверного отчета нам обязательно нужно, чтобы остаток «перетекал» из месяца в месяц, даже если в них не было оборотов.

Выясним, какими способами можно решить эту задачу в платформе «1С:Предприятие 8».

---

Решение №1: Использование механизмов СКД (дополнение периодов)

Если ваш отчет строится на базе Системы компоновки данных (СКД), то платформа берет на себя всю рутину по расчету и переносу остатков на «пустые» периоды. Это самый быстрый, надежный и рекомендуемый способ, который не создает лишней нагрузки на СУБД.

Разберем по шагам, как правильно настроить СКД для решения нашей задачи.

Шаг 1. Настройка ролей полей в наборе данных

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

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

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

Шаг 2. Настройка дополнения в структуре отчета

Теперь перейдем на вкладку «Настройки» в СКД, где определяется структура вывода данных:

  1. В структуре настроек добавьте группировку колонок по полю ПериодМесяц.
  2. Выделите созданную группировку и в окне свойств (обычно в правом нижнем углу экрана) найдите раздел «Параметры группировки».
  3. Установите свойство «Тип дополнения» в значение «Месяц» (или другой нужный вам период).
  4. Задайте свойства «Начало периода» и «Конец периода», связав их с соответствующими параметрами вашего отчета (например, &НачалоПериода и &КонецПериода).

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

---

Решение №2: Формирование «чистого» запроса с объединением точек остатка

Если перед вами стоит задача получить остатки программно в кодовой базе (например, для последующей выгрузки в JSON, отправки по API или формирования специфического табличного документа вручную), то стандартные механизмы СКД применить не удастся. Нам потребуется составить оптимальный SQL-запрос.

Для крупных баз данных самым производительным решением является сборка запроса через конструкцию ОБЪЕДИНИТЬ ВСЕ для каждого месяца. Мы получаем срез остатков на конкретные даты (границы месяцев) и объединяем их в одну результирующую таблицу.

Проанализируем пример кода на языке 1С, который динамически собирает такой запрос:


// Определяем массив дат, на которые нам нужны остатки (например, концы месяцев года)
МассивДат = Новый Массив;
ТекущаяДата = НачалоМесяца(ДатаНачала);
Пока ТекущаяДата <= КонецМесяца(ДатаОкончания) Цикл
    МассивДат.Добавить(КонецМесяца(ТекущаяДата));
    ТекущаяДата = ДобавитьМесяц(ТекущаяДата, 1);
КонецЦикла;

// Начинаем динамическое построение текста запроса
ТекстЗапроса = "";
ПараметрыЗапроса = Новый Структура;

Для Индекс = 0 По МассивДат.Количество() - 1 Цикл
    ИмяПараметра = "ДатаГраницы_" + Формат(Индекс, "ЧГ=0");
    ПараметрыЗапроса.Вставить(ИмяПараметра, МассивДат[Индекс]);
    
    Если Индекс > 0 Тогда
        ТекстЗапроса = ТекстЗапроса + "
        |ОБЪЕДИНИТЬ ВСЕ
        |";
    КонецЕсли;
    
    ТекстЗапроса = ТекстЗапроса + "
    |ВЫБРАТЬ
    |    &" + ИмяПараметра + " КАК ПериодМесяц,
    |    Остатки.Счет КАК Счет,
    |    Остатки.СуммаОстатокДт КАК СуммаОстатка
    |ИЗ
    |    РегистрБухгалтерии.Хозрасчетный.Остатки(&" + ИмяПараметра + ", Счет В (&СписокСчетов), , ) КАК Остатки";
КонецЦикла;

Запрос = Новый Запрос(ТекстЗапроса);
Для Каждого КлючИЗначение Из ПараметрыЗапроса Цикл
    Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;
Запрос.УстановитьПараметр("СписокСчетов", МассивИнтересующихСчетов);

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

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

---

Решение №3: Запрос с использованием производственного календаря

Еще один вариант получения непрерывных периодов в чистом запросе — это использование левого соединения с таблицей дат. В качестве источника непрерывного временного ряда обычно выступает производственный календарь (регистр сведений ДанныеПроизводственногоКалендаря) или аналогичная вспомогательная таблица.

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


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

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

Обратите внимание: данный способ требует аккуратности. Метод ДвиженияИГраницыПериода в параметрах виртуальной таблицы гарантирует нам наличие записей только на крайних точках (в начале и конце общего интервала). Если в середине года по какому-то субконто или счету не было движений в течение нескольких месяцев, прямое левое соединение вернет значения NULL для промежуточных месяцев, и нам придется дорабатывать алгоритм в запросе, чтобы «протянуть» предыдущие значения остатков.

---

Важный методологический нюанс: Корреспонденция в остатках

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

Остатки по своей природе не имеют корреспонденции!

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

Если бизнес-заказчику действительно требуется сопоставить остаток долга с приобретенными ценностями (например, отделить долг за материалы на счете 10.01 от долга за услуги), эту задачу решают двумя путями:

  1. Анализ по субконто «Документы расчетов с контрагентами». На счете 60 настраивается ведение детального учета по расчетным документам. В запросе мы получаем остатки на счете 60 в разрезе документов, а затем с помощью левого соединения с таблицей проводок (или оборотов) отфильтровываем только те документы расчетов, которые делали движения по дебету счета 10.01.
  2. Использование таблицы оборотов ДтКт. Если важна динамика закупок, а не конечный долг, мы используем виртуальную таблицу Хозрасчетный.ОборотыДтКт, где в параметрах четко указываем счет дебета (10.01) и счет кредита (60.01).

---

Резюме и рекомендации

Подведем итоги нашего разбора:

← На главную