Почему в отчете 1С не учитываются продажи за последний час суток с 23:00 до 00:00

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

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

Разбор ситуации и анализ исходного кода

Рассмотрим фрагмент кода, в котором происходит фильтрация полученных из запроса данных. Разработчик использует цикл для обработки таблицы значений ТЗ, где для каждой строки вычисляется время закрытия чека в секундах относительно начала дня:


Для Каждого Строка Из ТЗ Цикл
    Ч = (Строка.ДатаВремяЗакрытияЧека - НачалоДня(Строка.ДатаВремяЗакрытияЧека));
    Если Час(ВремяНач) < Час(ВремяКон) Тогда
        Если Ч < Час(ВремяНач) * 3600 ИЛИ Ч > Час(ВремяКон) * 3600 Тогда
            Продолжить;
        КонецЕсли;    
    КонецЕсли;
    // ... дальнейшая обработка
КонецЦикла;

На первый взгляд логика кажется верной: мы переводим часы начала и окончания интервала в секунды (умножая на 3600) и сравниваем с ними текущее время чека Ч. Однако именно здесь кроется критическая математическая ошибка, связанная с работой функции Час().

Выясним причину ошибки: ловушка функции Час()

Проанализируем, что происходит, когда пользователь устанавливает конечное время отчета как 23:59:59.

  1. Функция Час(ВремяКон) для значения "23:59:59" возвращает число 23.
  2. Выражение Час(ВремяКон) * 3600 превращается в 23 * 3600 = 82800 секунд.
  3. Значение 82800 секунд соответствует ровно 23:00:00.

Таким образом, условие Если Ч > Час(ВремяКон) * 3600 фактически превращается в "Если время чека больше 23:00:00, то пропустить эту строку". Рассмотрим пример: чек закрыт в 23:30:00. Переменная Ч для него будет равна 84600 секунд. Поскольку 84600 > 82800, срабатывает оператор Продолжить, и продажа исключается из отчета. Все данные за последний час суток (кроме самой первой секунды часа) отбрасываются алгоритмом.

Решение №1: Прямое сравнение времени (правильный подход)

Чтобы избежать ошибок округления до часа и некорректных математических вычислений, мы рекомендуем отказаться от использования функции Час() в операциях сравнения. В языке 1С время внутри типа Дата хранится как количество секунд. Рассмотрим, как переписать условие фильтрации правильно:


// Вычисляем границы интервала в секундах от начала дня один раз перед циклом
СекундНачала = ВремяНач - НачалоДня(ВремяНач);
СекундКонца = ВремяКон - НачалоДня(ВремяКон);

Для Каждого Строка Из ТЗ Цикл
    Ч = Строка.ДатаВремяЗакрытияЧека - НачалоДня(Строка.ДатаВремяЗакрытияЧека);
    
    Если СекундНачала < СекундКонца Тогда
        // Обычный интервал внутри одного дня
        Если Ч < СекундНачала ИЛИ Ч > СекундКонца Тогда
            Продолжить;
        КонецЕсли;
    ИначеЕсли СекундНачала > СекундКонца Тогда
        // Интервал, переходящий через полночь (например, с 22:00 до 02:00)
        Если НЕ (Ч > СекундНачала ИЛИ Ч < СекундКонца) Тогда
            Продолжить;
        КонецЕсли;
    КонецЕсли;
    // Обработка данных...
КонецЦикла;

В данном примере мы сравниваем чистые значения секунд. Для "23:59:59" переменная СекундКонца будет равна 86399, что позволит корректно обработать все чеки до последней секунды дня включительно.

Решение №2: Фильтрация на уровне SQL-запроса

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

Лучшим решением будет перенос условий по времени непосредственно в текст Запрос.Текст. Это позволит серверу базы данных (например, MS SQL или PostgreSQL) самостоятельно отсечь ненужные записи. В языке запросов 1С для внешних источников можно использовать функции работы с датами, но стоит учитывать специфику диалекта SQL конкретной СУБД.

Если же мы работаем с обычными таблицами 1С, то параметры &ДатаНачала и &ДатаОкончания должны сразу включать в себя время. Обратите внимание на установку параметров в исходном коде:


Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаКонца));

Здесь КонецДня возвращает "23:59:59". Если в самом тексте запроса в секции ГДЕ уже стоит ограничение по времени, то дополнительная проверка в цикле Для Каждого становится избыточной и только вносит риск ошибки, как это и произошло в рассматриваемом случае.

Дополнительные рекомендации по коду

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

  1. Накопительный итог в макете: В коде используется странная конструкция МакетСтроки.Параметры["Кол10"] = Кол7 + Кол8 + Кол10;. Это приводит к тому, что в колонке за 10 утра выводятся продажи за все предыдущие часы. Если ваша задача — показать чистые почасовые продажи, уберите суммирование переменных.
  2. Поиск номенклатуры в цикле: Использование Справочники.Номенклатура.НайтиПоКоду внутри цикла по каждой строке чеков — это "антипаттерн" производительности. Программа будет работать очень медленно. Правильнее будет выгрузить все уникальные коды товаров из ТЗ, одним запросом получить ссылки на номенклатуру и сохранить их в Соответствие (ключ — код, значение — ссылка).
  3. Обработка "24 часа": В коде встречается условие ИначеЕсли Строка.Время = 24 Тогда. Помните, что в системе 1С час может принимать значения только от 0 до 23. Час "24" может возникнуть только при ручном формировании строк или специфической логике внешней системы, но штатные функции 1С его не вернут.

Таким образом, основная проблема "пропажи" данных с 23:00 до полуночи решается исправлением математической формулы сравнения времени. Используйте разницу между датой и началом дня для получения точного количества секунд и избегайте необоснованного округления через Час().

← На главную