Почему остатки по счету в запросе 1С не сходятся с оборотно-сальдовой ведомостью?

Программист 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

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

Вот пример исходного кода, который приводит к ошибке:


 Функция ПолучитьОстатокПоСчету(ТекСчет,ТекДата)
    Запрос = Новый Запрос; 
    Запрос.Текст = "ВЫБРАТЬ 
    |    ХозрасчетныйОстатки.СуммаОстатокДт, 
    |    ХозрасчетныйОстатки.СуммаОстатокКт 
    |ИЗ 
    |    РегистрБухгалтерии.Хозрасчетный.Остатки( &КонПериода, Счет = &Счет,,) КАК ХозрасчетныйОстатки"; 
    Запрос.УстановитьПараметр("КонПериода", ТекДата); // ТекДата установлена в конец дня, например, '31.12.2023 23:59:59'
    Запрос.УстановитьПараметр("Счет", ТекСчет); 
    РезультатЗапроса = Запрос.Выполнить();
    выборка=РезультатЗапроса.выбрать();
    Если выборка.Следующий() Тогда
        Возврат выборка.СуммаОстатокДт;    
    КонецЕсли;
    
    Возврат 0;
КонецФункции

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

Причина №1: Неправильное позиционирование на временной шкале

Это самая частая и неочевидная ошибка. Когда мы передаем в параметр виртуальной таблицы Остатки обычный объект типа Дата (например, значение '31.12.2023 23:59:59'), система 1С интерпретирует эту точку как начало секунды. Запрос возвращает остатки на момент до совершения любых операций в эту секунду.

Если в системе есть документы, проведенные ровно в 23:59:59, их движения не попадут в расчет остатка. Отчет же (ОСВ) формирует данные на конец периода включительно, то есть учитывает все документы, проведенные в последнюю секунду. Отсюда и возникает расхождение.

Решение: использование объекта «Граница»

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

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

Модифицируем установку параметра периода в нашем запросе:


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

Такой подход гарантирует, что запрос вернет остаток после проведения всех документов, датированных указанным временем.

Альтернативный, но менее надежный способ

Иногда можно встретить рекомендацию просто прибавить одну секунду к дате окончания периода. Например, если нам нужен остаток на конец дня 31.12.2023, мы запрашиваем остаток на начало дня 01.01.2024. Это сработает в большинстве случаев, но является менее "академичным" решением и может дать сбой, если существуют документы, проведенные ровно в 00:00:00 следующего дня.

Причина №2: Отсутствие обязательных фильтров в запросе

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

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

То же самое касается и других важных аналитических разрезов:

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

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

Итоговый правильный код

Давайте соберем все рекомендации вместе и напишем исправленную, надежную функцию. Добавим в нее параметр ТекОрганизация и будем использовать объект Граница.


Функция ПолучитьОстатокПоСчету(ТекСчет, ТекДата, ТекОрганизация)

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

    // Создаем границу, чтобы включить документы, проведенные в последнюю секунду
    КонецПериода = Новый Граница(КонецДня(ТекДата), ВидГраницы.Включая);

    Запрос.УстановитьПараметр("КонПериода", КонецПериода);
    Запрос.УстановитьПараметр("Счет", ТекСчет);
    Запрос.УстановитьПараметр("Организация", ТекОрганизация);

    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        // Если по счету не было движений, остаток равен 0
        Возврат 0;
    Иначе
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        // В зависимости от типа счета, может быть как дебетовый, так и кредитовый остаток.
        // Для активных счетов обычно возвращают дебетовый остаток.
        Возврат Выборка.СуммаОстатокДт; 
        // Или, если нужно конечное сальдо:
        // Возврат Выборка.СуммаОстаток;
    КонецЕсли;

КонецФункции

Важные улучшения в этом коде:

Таким образом, для получения корректных остатков, всегда сверяйте параметры вашего запроса (период, организация, аналитика) с настройками отчета, с которым вы сравниваете результат, и используйте объект Граница для точного определения конца периода.

← На главную