При разработке подсистем учета времени, составления графиков или расписаний часто возникает задача проверки наложения данных. Например, требуется исключить ситуации, когда один кабинет, преподаватель или студенческая группа назначаются на одну и ту же пару одновременно. Проанализируем типичные ошибки при написании подобной проверки и рассмотрим правильные механизмы контроля как в табличной части документа, так и с использованием регистров сведений.
Рассмотрим распространенный фрагмент кода, в котором разберем ошибки логики и синтаксиса:
СтрТабЧасти = Элементы.Расписание.ТекущиеДанные;
Если СтрТабЧасти.Предмет = СтрТабЧасти.Предмет Тогда
Если СтрТабЧасти.НомерПары = СтрТабЧасти.НомерПары Тогда
Отказ = Истина;
Возврат;
Сообщить = ("Дисциплина занята другой группой");
КонецЕсли;
КонецЕсли;
Выясним основные причины, по которым данный код не выполняет поиск дублей по всей таблице:
СтрТабЧасти.Предмет = СтрТабЧасти.Предмет сравнивает значение в текущей редактируемой строке с этим же значением из той же строки. Это условие всегда возвращает Истина и не ищет совпадения в других строках.Элементы.Расписание.ТекущиеДанные возвращает только одну текущую строку табличной части. Он не предоставляет доступ ко всей коллекции для поиска пересечений.Возврат; размещен перед синтаксически неверным вызовом сообщения. До функции информирования пользователя управление не дойдет. Вызов синтаксически пишется как Сообщить("Текст").Отказ недоступна в событиях элементов формы (например, ПриИзменении). Она используется только в специализированных событиях формы или объекта.Разберем практические способы, которые позволяют проверить совпадение данных по всей табличной части документа.
Способ 1. Использование метода НайтиСтроки()
Метод НайтиСтроки() позволяет найти все строки, соответствующие заданной структуре отбора. Проанализируем пример выполнения такой проверки:
ТекущаяСтрока = Элементы.Расписание.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
Отбор = Новый Структура;
Отбор.Вставить("ДеньНедели", ТекущаяСтрока.ДеньНедели);
Отбор.Вставить("НомерПары", ТекущаяСтрока.НомерПары);
Отбор.Вставить("Кабинет", ТекущаяСтрока.Кабинет);
НайденныеСтроки = Объект.Расписание.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 1 Тогда
СообщитьПользователю = Новый СообщениеПользователю;
СообщитьПользователю.Текст = "Выбранный кабинет уже занят на эту пару!";
СообщитьПользователю.Сообщить();
ТекущаяСтрока.Кабинет = Справочники.Кабинеты.ПустаяСсылка();
КонецЕсли;
Способ 2. Обход коллекции табличной части в цикле
Посмотрим на пример обхода всей табличной части с исключением текущей редактируемой строки:
ТекущаяСтрока = Элементы.Расписание.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого СтрИзТабЧасти Из Объект.Расписание Цикл
Если СтрИзТабЧасти = ТекущаяСтрока Тогда
Продолжить;
КонецЕсли;
Если СтрИзТабЧасти.ДеньНедели = ТекущаяСтрока.ДеньНедели
И СтрИзТабЧасти.НомерПары = ТекущаяСтрока.НомерПары Тогда
Если СтрИзТабЧасти.Кабинет = ТекущаяСтрока.Кабинет И ЗначениеЗаполнено(ТекущаяСтрока.Кабинет) Тогда
ВызватьИсключение "Кабинет уже занят!";
КонецЕсли;
КонецЕсли;
КонецЦикл;
Способ 3. Проверка на сервере через запросы или Таблицу Значений
При проверке документа перед записью в событии ОбработкаПроверкиЗаполнения или ПередЗаписьюНаСервере выгрузим табличную часть и сгруппируем данные. Если результат группировки показывает количество записей больше 1 для одного временного слота, установим параметр Отказ = Истина.
Рассмотрим, в каких событиях формы и объекта следует вызывать проверки:
Отказ здесь нет, при обнаружении коллизии следует очищать введенное значение и информировать пользователя с помощью СообщениеПользователю.Отказ. Установка Отказ = Истина блокирует завершение редактирования строки до тех пор, пока пользователь не исправит наложение.Хотя проверку можно реализовать внутри табличной части документа, для задач составления расписания оптимальным архитектурным решением является использование объектов РегистрСведений. Хранение данных в регистре предотвращает наложение занятости между разными документами и оперативно показывает текущую загрузку аудиторий и преподавателей.
Для реализации логики контроля расписания спроектируем следующую структуру регистров сведений:
Регистр 1: ЗанятостьГрупп (периодический или независимый)
ДеньНедели, НомерПары, ГруппаПредмет, Преподаватель, КабинетРегистр 2: ЗанятостьКабинетов
ДеньНедели, НомерПары, КабинетГруппа, ПредметПри такой структуре проверка сводится к выполнению быстрого точечного запроса или вызову метода Получить() менеджера регистра сведений. Это позволяет мгновенно определить, свободен ли кабинет или преподаватель на указанную пару, не выполняя ресурсных обходов табличных частей документов.