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

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

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

Почему таблица ОстаткиИОбороты работает именно так

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

Способ 1. Использование временных таблиц и Момента Времени

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

Шаг 1. Создаем временную таблицу со всеми необходимыми данными и моментом времени.


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

Шаг 2. Находим максимальный момент времени для каждой аналитики (например, для каждой номенклатуры на складе).


ВЫБРАТЬ
    ВТ_Данные.Склад КАК Склад,
    ВТ_Данные.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ВТ_Данные.МоментВремени) КАК МаксМомент
ПОМЕСТИТЬ ВТ_ПоследниеДвижения
ИЗ
    ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
    ВТ_Данные.Склад,
    ВТ_Данные.Номенклатура
;

Шаг 3. Соединяем исходные данные с таблицей остатков, чтобы получить итоговое значение.

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

Способ 2. Объединение таблиц (метод UNION)

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

  1. Выбираем начальные остатки на &ДатаНач.
  2. Выбираем обороты за период с детализацией по регистратору.
  3. Выбираем конечные остатки на &ДатаКон.

Проанализируем пример кода:


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

ОБЪЕДИНИТЬ ВСЕ

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

ОБЪЕДИНИТЬ ВСЕ

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

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

Способ 3. Левое соединение Оборотов и Остатков

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

Схема запроса будет выглядеть так:

  1. В основной таблице выбираем данные из РегистрНакопления.ТоварыНаСкладах.Обороты(..., Регистратор).
  2. Присоединяем к ней таблицу РегистрНакопления.ТоварыНаСкладах.Остатки(&КонецПериода) по полям Номенклатура и Склад.

Важные рекомендации по работе с датами

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

Использование СКД для решения задачи

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

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

← На главную