Почему запрос остатков в 1С выдает некорректные данные в течение дня?

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

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

Понимание работы регистров накопления и времени в 1С

Для того чтобы разобраться в корне проблемы, нам необходимо углубиться в то, как 1С хранит и обрабатывает данные в регистрах накопления – ключевом механизме для учета числовых показателей с привязкой ко времени. Мы проанализируем основные особенности, влияющие на расчет остатков.

  1. Поле "Период" в движениях регистра. Мы выясним, что каждая запись, которую мы называем "движением", в регистре накопления содержит поле Период. Это поле не просто дата, это полноценная дата и время совершения операции. Именно этот реквизит, в сочетании с Регистратором (ссылкой на документ, который создал движение), определяет точный момент времени, когда произошло изменение данных в регистре.
  2. Виртуальная таблица "Остатки": её специфические особенности. Мы разберем, как работает виртуальная таблица Остатки регистра накопления. Ее основная задача – рассчитывать итоговые остатки ресурсов на определенный момент времени. Критически важно понимать, что по умолчанию виртуальная таблица Остатки строит свои итоги на начало секунды, которая указана в параметре Период. Это означает, что она *не включает* в расчет само указанное время. Например, если мы запрашиваем остатки на 06.05.2024 15:20:17, то будут учтены все движения, произошедшие *до* этого момента, но движения, случившиеся *в* 15:20:17 или позже, учтены не будут. Если же в параметр Период передается только дата без времени (например, 06.05.2024), остатки будут рассчитаны на начало этого дня (06.05.2024 00:00:00), не учитывая никаких движений, произошедших в течение дня.
  3. "Фича" движений на конец дня (23:59:59). Мы обратим внимание на очень распространенную архитектурную особенность некоторых типовых конфигураций 1С (в частности, в УТ 10, как упомянуто в теме). В них существует практика записи движений в регистры накопления на 23:59:59 текущего дня. Это делается не случайно:
    • Для целостности на начало следующего дня: Такой подход гарантирует, что на начало следующего дня (07.05.2024 00:00:00) все расчеты за предыдущий день будут закрыты и корректно сформированы.
    • Удобство для операторов: Это избавляет операторов от необходимости вручную распределять документы по точному времени в течение дня, что в большинстве случаев не является бизнес-требованием для бухгалтерского или управленческого учета. Платежные документы, например, часто загружаются из банка раз в день, и нет необходимости привязывать их к точному времени проведения.

    Именно из-за этой особенности движения за текущий день, "перенесенные" на 23:59:59, не попадают в остатки, запрошенные на любую дату/время *до* 23:59:59 этого дня. Это и приводит к той самой "каше", когда мы видим неактуальные данные в течение дня.

Причины "каши" в течение дня: Разбираем на примере

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

Автор приводит пример: "ВКонДата указываю ДатаДокумента-1 (получается 06.05.24 15:20:17, сама реализация 06.05.24 15:20:18) долг показывает как 278 000 (от реализации 24.04.24, по которой была оплата 06.05.24 00:00:00)".

  1. Запрос на 06.05.24 15:20:17: Почему не видна оплата?

    Как мы уже выяснили, виртуальная таблица Остатки по умолчанию берет данные *на начало* указанной секунды. Если оплата в 06.05.24 00:00:00 была зафиксирована в регистре, то она должна была бы быть учтена. Однако, сообщение автора "движ в 23.59.59" является ключевым. Скорее всего, само движение по взаиморасчетам, связанное с оплатой, не было зафиксировано точно в 00:00:00, а было "перенесено" на 06.05.24 23:59:59. В таком случае, при запросе остатков на 15:20:17, эта оплата просто еще не попадает в расчеты, так как ее "официальное" время еще не наступило с точки зрения регистра накопления.

    Поэтому долг продолжает отображаться как 278 000, игнорируя фактически произошедшую, но еще не "записанную" в регистр на текущий момент времени оплату. На самом деле, оплата была, но движение в регистре появится только в конце дня.

  2. После 06.05.24 15:20:19: Почему остаток неправильно считается?

    Автор говорит: "после 06.05.24 15:20:19 показывает долг как 278 000 + 235 000". Здесь мы видим, что новая реализация (235 000) уже учтена, так как ее время (15:20:18) уже прошло. Но при этом оплата (которая должна была уменьшить долг 278 000 до нуля) все еще не учтена. Это подтверждает нашу гипотезу о том, что движения, связанные с погашением долга, фиксируются в конце дня. Таким образом, запрос видит новую задолженность, но не видит ее погашения, создавая впечатление "конечного остатка, но без учета оплаты".

  3. Корректное отображение на 05.05.24 и 07.05.24: В чем причина?

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

Решение проблемы: Используем объект "Граница"

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

  1. Что такое Граница?

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

  2. Использование ВидГраницы.Включая для остатков на конец периода.

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

    Пример запроса:

    
    Запрос.Текст = "ВЫБРАТЬ
        |    ВзаиморасчетыСКонтрагентамиОстатки.Организация,
        |    ВзаиморасчетыСКонтрагентамиОстатки.Контрагент,
        |    ВзаиморасчетыСКонтрагентамиОстатки.СуммаВзаиморасчетовОстаток
        |ИЗ
        |    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(
        |        &ГраницаПериода,
        |        Организация = &Организация
        |        И Контрагент = &Контрагент) КАК ВзаиморасчетыСКонтрагентамиОстатки";
    |
    // Инициализация параметра ГраницаПериода:
    Запрос.УстановитьПараметр("ГраницаПериода", Новый Граница(КонецДня(ДатаОтчета), ВидГраницы.Включая));
    // Где ДатаОтчета - это та дата, на конец которой нам нужен остаток, например, 06.05.2024
    

    Здесь функция КонецДня(ДатаОтчета) преобразует указанную дату в момент ДД.ММ.ГГГГ 23:59:59. Затем Новый Граница(..., ВидГраницы.Включая) указывает системе, что мы хотим учесть все движения, которые произошли *вплоть до* и *включая* этот момент времени. Таким образом, даже если движение по оплате было зафиксировано в 23:59:59, оно будет учтено.

  3. Использование Границы на момент времени документа.

    Иногда нам требуется получить остатки, которые включают движения конкретного документа, и получить их *сразу после* проведения этого документа. В этом случае мы можем использовать специальный метод МоментВремени(), доступный для ссылок на документы, и передать его в объект Граница.

    Пример инициализации параметра:

    
    Запрос.УстановитьПараметр("ГраницаПериода", Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Включая));
    // Где СсылкаНаДокумент - это ссылка на документ, движения которого мы хотим учесть
    

    Объект МоментВремени() является уникальным идентификатором, который сочетает в себе дату и время проведения документа с его уникальной ссылкой. Это обеспечивает максимально точное позиционирование во времени и гарантирует, что остатки будут рассчитаны именно после проведения данного документа, включая все его движения. Использование ВидГраницы.Включая в данном случае обеспечит учет движений самого документа.

Альтернативные подходы и диагностика

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

  1. Добавление секунды к дате/времени (устаревший подход).

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

    Например, если нам нужны остатки на 06.05.24 15:20:17, мы могли бы передать в запрос 06.05.24 15:20:18.

    
    Запрос.УстановитьПараметр("ВКонДата", ДатаДокумента + 1); // Если ДатаДокумента - это объект типа Дата/Время
    

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

  2. Использование виртуальной таблицы "ОстаткиИОбороты".

    Виртуальная таблица ОстаткиИОбороты предоставляет более комплексную информацию, позволяя получить как начальные остатки, так и обороты за период, и конечные остатки. Она также поддерживает использование объекта Граница для своих параметров НачалоПериода и КонецПериода. Эта таблица может быть полезна, когда нам нужно не просто получить остаток на определенный момент, но и проанализировать динамику изменений за какой-либо интервал времени.

    Пример использования:

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

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

  3. Просмотр фактических движений регистра накопления.

    Мы настоятельно рекомендуем, как это было предложено в обсуждении на форуме (сообщение 3 и 8), всегда начинать диагностику с просмотра фактических движений регистра накопления. Это самый прямой способ понять, что именно и в какой момент времени было записано в базу данных.

    Вы можете сделать это несколькими способами:

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

    Именно такой просмотр позволил автору темы (сообщение 4) быстро подтвердить причину проблемы: "спасибо действительно движ в 23.59.59". Эта информация является критически важной для принятия правильного решения.

Итоги и рекомендации

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

  1. Особенностями записи движений в регистры накопления на конец дня (23:59:59) в некоторых типовых конфигурациях, таких как УТ 10.
  2. Поведением виртуальной таблицы Остатки, которая по умолчанию рассчитывает итоги на начало указанного момента времени, исключая его.

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

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

← На главную