В процессе разработки отчетов по почасовым продажам программисты часто сталкиваются с ситуацией, когда данные за определенные периоды (чаще всего за последний час суток) бесследно исчезают из финальной таблицы. Проанализируем типичный случай, когда при формировании отчета по чековой ленте из внешнего источника данных (системы SIS) продажи в интервале с 23:00:00 до 23:59:59 не попадают в результат.
Рассмотрим фрагмент кода, в котором происходит фильтрация полученных из запроса данных. Разработчик использует цикл для обработки таблицы значений ТЗ, где для каждой строки вычисляется время закрытия чека в секундах относительно начала дня:
Для Каждого Строка Из ТЗ Цикл
Ч = (Строка.ДатаВремяЗакрытияЧека - НачалоДня(Строка.ДатаВремяЗакрытияЧека));
Если Час(ВремяНач) < Час(ВремяКон) Тогда
Если Ч < Час(ВремяНач) * 3600 ИЛИ Ч > Час(ВремяКон) * 3600 Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
// ... дальнейшая обработка
КонецЦикла;
На первый взгляд логика кажется верной: мы переводим часы начала и окончания интервала в секунды (умножая на 3600) и сравниваем с ними текущее время чека Ч. Однако именно здесь кроется критическая математическая ошибка, связанная с работой функции Час().
Проанализируем, что происходит, когда пользователь устанавливает конечное время отчета как 23:59:59.
Час(ВремяКон) для значения "23:59:59" возвращает число 23.Час(ВремяКон) * 3600 превращается в 23 * 3600 = 82800 секунд.Таким образом, условие Если Ч > Час(ВремяКон) * 3600 фактически превращается в "Если время чека больше 23:00:00, то пропустить эту строку". Рассмотрим пример: чек закрыт в 23:30:00. Переменная Ч для него будет равна 84600 секунд. Поскольку 84600 > 82800, срабатывает оператор Продолжить, и продажа исключается из отчета. Все данные за последний час суток (кроме самой первой секунды часа) отбрасываются алгоритмом.
Чтобы избежать ошибок округления до часа и некорректных математических вычислений, мы рекомендуем отказаться от использования функции Час() в операциях сравнения. В языке 1С время внутри типа Дата хранится как количество секунд. Рассмотрим, как переписать условие фильтрации правильно:
// Вычисляем границы интервала в секундах от начала дня один раз перед циклом
СекундНачала = ВремяНач - НачалоДня(ВремяНач);
СекундКонца = ВремяКон - НачалоДня(ВремяКон);
Для Каждого Строка Из ТЗ Цикл
Ч = Строка.ДатаВремяЗакрытияЧека - НачалоДня(Строка.ДатаВремяЗакрытияЧека);
Если СекундНачала < СекундКонца Тогда
// Обычный интервал внутри одного дня
Если Ч < СекундНачала ИЛИ Ч > СекундКонца Тогда
Продолжить;
КонецЕсли;
ИначеЕсли СекундНачала > СекундКонца Тогда
// Интервал, переходящий через полночь (например, с 22:00 до 02:00)
Если НЕ (Ч > СекундНачала ИЛИ Ч < СекундКонца) Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
// Обработка данных...
КонецЦикла;
В данном примере мы сравниваем чистые значения секунд. Для "23:59:59" переменная СекундКонца будет равна 86399, что позволит корректно обработать все чеки до последней секунды дня включительно.
Разберем ситуацию более глобально. В исходном коде используется ВнешнийИсточникДанных.SIS. Выполнение фильтрации внутри цикла 1С после получения всех данных — это крайне неэффективный метод, особенно для больших розничных сетей. Проанализируем, как можно оптимизировать этот процесс.
Лучшим решением будет перенос условий по времени непосредственно в текст Запрос.Текст. Это позволит серверу базы данных (например, MS SQL или PostgreSQL) самостоятельно отсечь ненужные записи. В языке запросов 1С для внешних источников можно использовать функции работы с датами, но стоит учитывать специфику диалекта SQL конкретной СУБД.
Если же мы работаем с обычными таблицами 1С, то параметры &ДатаНачала и &ДатаОкончания должны сразу включать в себя время. Обратите внимание на установку параметров в исходном коде:
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаКонца));
Здесь КонецДня возвращает "23:59:59". Если в самом тексте запроса в секции ГДЕ уже стоит ограничение по времени, то дополнительная проверка в цикле Для Каждого становится избыточной и только вносит риск ошибки, как это и произошло в рассматриваемом случае.
Проанализируем еще несколько проблемных мест в представленном алгоритме:
МакетСтроки.Параметры["Кол10"] = Кол7 + Кол8 + Кол10;. Это приводит к тому, что в колонке за 10 утра выводятся продажи за все предыдущие часы. Если ваша задача — показать чистые почасовые продажи, уберите суммирование переменных.Справочники.Номенклатура.НайтиПоКоду внутри цикла по каждой строке чеков — это "антипаттерн" производительности. Программа будет работать очень медленно. Правильнее будет выгрузить все уникальные коды товаров из ТЗ, одним запросом получить ссылки на номенклатуру и сохранить их в Соответствие (ключ — код, значение — ссылка).ИначеЕсли Строка.Время = 24 Тогда. Помните, что в системе 1С час может принимать значения только от 0 до 23. Час "24" может возникнуть только при ручном формировании строк или специфической логике внешней системы, но штатные функции 1С его не вернут.Таким образом, основная проблема "пропажи" данных с 23:00 до полуночи решается исправлением математической формулы сравнения времени. Используйте разницу между датой и началом дня для получения точного количества секунд и избегайте необоснованного округления через Час().