При разработке механизмов планирования, журналов записи клиентов или медицинских расписаний часто возникает задача: взять общий интервал времени приема из одной табличной части (например, с 09:00 до 18:00) и разбить его на отдельные слоты фиксированной длительности (например, по 30 или 40 минут) во второй табличной части. При реализации подобного алгоритма разработчики нередко сталкиваются с логическими ошибками в условиях, некорректной работой с типом Дата и возникновением бесконечных циклов.
Разберем по шагам, почему возникают эти ошибки, как правильно проверять заполнение полей времени и как выстроить корректный алгоритм генерации строк расписания.
Рассмотрим фрагмент кода, с которого часто начинаются проблемы:
Если ЗначениеЗаполнено(СтрокаТабПриемов.Время1) или (СтрокаТабПриемов.Время2) Тогда
Продолжить;
КонецЕсли;
В данном фрагменте допущены сразу две критические ошибки:
или (СтрокаТабПриемов.Время2) пытается привести значение поля Время2 (имеющего тип Дата) к типу Булево. Платформа 1С не выполняет неявное приведение дат к логическому типу, из-за чего возникает исключение времени выполнения: «Преобразование значения к типу Булево не может быть выполнено». Функция проверки должна вызываться для каждого операнда отдельно.Не, мы заставляем цикл вызывать оператор Продолжить как раз тогда, когда время заполнено. В результате обработка полезных данных полностью пропускается.Также отметим распространенную ошибку начинающих разработчиков — попытку вызова метода Пустая() у даты (например, СтрокаТабПриемов.Время1.Пустая()). Метод Пустая() существует только у ссылочных типов данных (справочники, документы и др.). Для проверки даты на значение 01.01.0001 00:00:00 следует использовать глобальную функцию ЗначениеЗаполнено() либо прямое сравнение с Дата(1, 1, 1).
Правильный вариант проверки заполненности обоих полей времени выглядит следующим образом:
Если Не ЗначениеЗаполнено(СтрокаТабПриемов.Время1) Или Не ЗначениеЗаполнено(СтрокаТабПриемов.Время2) Тогда
Продолжить; // Пропускаем строки, где не указано начало или окончание
КонецЕсли;
Тип данных Дата в 1С хранит одновременно дату и время с точностью до секунды. Любые математические операции сложения и вычитания с числом платформа производит в секундах:
ТекущаяДата + 60 увеличивает время ровно на одну минуту, а ТекущаяДата + (40 * 60) — на 40 минут. Никаких сложных строковых преобразований и ручных вычислений остатков делать не требуется.Дата2 - Дата1 возвращает число секунд между двумя моментами времени.Если выполнять избыточные операции вроде ч1 = мЧ; ч2 = мЧ - ч1;, переменная ч2 всегда будет равна 0, а пересчет минут через деление на 100 приведет к некорректным значениям времени.
Проанализируем правильную реализацию цикла генерации интервалов с учетом защиты от зацикливания и граничных условий:
// 1. Очищаем результирующую таблицу перед заполнением
Объект.ТабВремя.Очистить();
// 2. Проверяем валидность параметров генерации
Если Объект.Продолжительность <= 0 Тогда
Сообщить("Укажите положительную продолжительность приема в минутах!");
Возврат;
КонецЕсли;
ШагИнтервалаСекунд = Объект.Продолжительность * 60;
// 3. Обходим строки исходной таблицы
Для Каждого СтрокаТабПриемов Из Объект.ТабПриемов Цикл
// Проверяем заполненность реквизитов
Если Не ЗначениеЗаполнено(СтрокаТабПриемов.Время1) Или Не ЗначениеЗаполнено(СтрокаТабПриемов.Время2) Тогда
Продолжить;
КонецЕсли;
// Проверяем корректность временного диапазона
Если СтрокаТабПриемов.Время1 >= СтрокаТабПриемов.Время2 Тогда
Продолжить;
КонецЕсли;
ТекущееВремя = СтрокаТабПриемов.Время1;
// 4. Генерируем слоты в цикле
Пока ТекущееВремя < СтрокаТабПриемов.Время2 Цикл
ВремяОкончанияСлота = ТекущееВремя + ШагИнтервалаСекунд;
// Ограничиваем слот временем окончания приема
Если ВремяОкончанияСлота > СтрокаТабПриемов.Время2 Тогда
ВремяОкончанияСлота = СтрокаТабПриемов.Время2;
КонецЕсли;
НоваяСтрока = Объект.ТабВремя.Добавить();
НоваяСтрока.ДатаПриема = СтрокаТабПриемов.ДатаПриема;
НоваяСтрока.НазвДня = СтрокаТабПриемов.НазвДня;
НоваяСтрока.Кабинет = СтрокаТабПриемов.Кабинет;
НоваяСтрока.Кресло = СтрокаТабПриемов.Кресло;
НоваяСтрока.ВремяН = ТекущееВремя;
НоваяСтрока.ВремяК = ВремяОкончанияСлота;
// Сдвигаем текущее время на шаг вперед
ТекущееВремя = ВремяОкончанияСлота;
КонецЦикла;
КонецЦикла;
Если Объект.ТабВремя.Количество() = 0 Тогда
Сообщить("В таблице расписания нет корректно заполненных интервалов!");
КонецЕсли;
Продолжительность строго больше нуля до входа в цикл Пока. Если к дате прибавлять 0, условие выхода никогда не выполнится, что приведет к зависанию клиента 1С.ТабВремя.Очистить(), чтобы избежать дублирования строк при повторном нажатии на кнопку формирования.Время2, либо отброшен в зависимости от бизнес-логики задачи.