Как правильно реализовать проверку занятости аудитории и преподавателя в расписании 1С?

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

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

Анализ основной ошибки: почему СрезПоследних не подходит для расписания

Рассмотрим ситуацию, описанную в коде автора. Основная техническая проблема заключается в использовании виртуальной таблицы СрезПоследних для проверки свободного времени. В системе 1С таблица СрезПоследних предназначена для получения актуальных значений ресурсов на конкретный момент времени. Если в регистре есть запись за «вчера», а на «сегодня» данных нет, СрезПоследних на сегодня вернет вчерашнюю запись, так как она является последней актуальной.

Для периодических процессов типа расписания, где важна фиксация события именно в конкретный момент времени (час, день, пара), использование этой таблицы ошибочно. Нам необходимо проверять не «последнее состояние», а наличие конкретной записи в определенном интервале или на конкретную дату. Проанализируем, как изменить подход к получению данных.

Шаг 1. Переход к физической таблице регистра

Вместо виртуальной таблицы РегистрСведений.РасписаниеЗанятий.СрезПоследних мы должны использовать основную физическую таблицу регистра. Это позволит нам четко задать условие: «есть ли запись ровно на это время?». Если мы ищем пересечения, нам не нужны данные за предыдущие периоды.

Посмотрим, как должен выглядеть фрагмент условия в запросе:


ИЗ
    РегистрСведений.РасписаниеЗанятий КАК Расписание
ГДЕ
    Расписание.Период = &НужныйМоментВремени
    И (Расписание.Аудитория = &Аудитория ИЛИ Расписание.Преподаватель = &Преподаватель)

Шаг 2. Оптимизация: исключаем запрос в цикле

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

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

  1. Выгружаем табличную часть документа в таблицу значений.
  2. Передаем эту таблицу в запрос как параметр.
  3. В запросе помещаем данные во временную таблицу.
  4. Соединяем временную таблицу с физической таблицей регистра сведений по полям времени, аудитории и преподавателя.

Шаг 3. Учет текущего документа (самоблокировка)

При перепроведении документа система может выдать ошибку «Аудитория занята», найдя записи, сделанные этим же самым документом ранее. Чтобы этого избежать, в запрос обязательно нужно добавить условие исключения текущего регистратора. Рассмотрим пример условия:


ГДЕ
    Расписание.Регистратор <> &СсылкаНаДокумент

Шаг 4. Исправление логики «Час» и «День»

Если в вашем регистре установлена периодичность В пределах дня, то стандартное поле Период хранит только дату. Использование отдельного измерения Час допустимо, но усложняет логику. Рекомендуется использовать периодичность Секунда, где в поле Период будет записываться полная дата и время начала занятия. Это позволит использовать стандартные механизмы работы с периодами 1С.

Если же вы используете измерение Час, убедитесь, что в запросе вы проверяете соответствие и дня, и часа одновременно. В коде из примера автора условие ИЛИ было поставлено не совсем удачно. Перепишем логику условий для большей надежности:


ГДЕ
    Расписание.День = &День
    И Расписание.Регистратор <> &Ссылка
    И (Расписание.Час = &Час И (Расписание.Аудитория = &Аудитория ИЛИ Расписание.Преподаватель = &Преподаватель))

Шаг 5. Реализация блокировок данных

Проанализируем ситуацию параллельной работы. Если два методиста одновременно проводят документы, в которых указана одна и та же аудитория на одно и то же время, запросы обоих могут показать, что место свободно (так как данные еще не записаны в БД). Чтобы предотвратить «накладки», необходимо использовать объект БлокировкаДанных.

Перед выполнением запроса в процедуре ОбработкаПроведения добавим следующий код:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.РасписаниеЗанятий");
ЭлементБлокировки.УстановитьПараметр("День", День);
// Если аудиторий много, можно заблокировать конкретные
ЭлементБлокировки.ИсточникДанных = Предметы;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Аудитория", "Аудитория");
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Час", "Час");
Блокировка.Заблокировать();

Итоговый пример правильной структуры процедуры проведения

Соберем все рекомендации воедино и посмотрим, как должна выглядеть качественная процедура ОбработкаПроведения с точки зрения профессиональной разработки на 1С:


Процедура ОбработкаПроведения(Отказ, Режим)
    
    // 1. Подготовка данных табличной части
    ТаблицаЗанятий = Предметы.Выгрузить();
    
    // 2. Установка управляемых блокировок
    Блокировка = Новый БлокировкаДанных;
    Элемент = Блокировка.Добавить("РегистрСведений.РасписаниеЗанятий");
    Элемент.ИсточникДанных = ТаблицаЗанятий;
    Элемент.ИспользоватьИзИсточникаДанных("Аудитория", "Аудитория");
    Элемент.ИспользоватьИзИсточникаДанных("Час", "Час");
    Блокировка.Заблокировать();
    
    // 3. Формирование запроса к ФИЗИЧЕСКОЙ таблице
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЧ.НомерСтроки КАК НомерСтроки,
    |    ТЧ.Аудитория КАК Аудитория,
    |    ТЧ.Преподаватель КАК Преподаватель,
    |    ТЧ.Час КАК Час
    |ПОМЕСТИТЬ ВТ_ДанныеДокумента
    |ИЗ
    |    &ТаблицаЗанятий КАК ТЧ
    |;
    |ВЫБРАТЬ
    |    ВТ.НомерСтроки,
    |    Расписание.Аудитория КАК ЗанятаяАудитория,
    |    Расписание.Преподаватель КАК ЗанятыйПреподаватель
    |ИЗ
    |    ВТ_ДанныеДокумента КАК ВТ
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.РасписаниеЗанятий КАК Расписание
    |    ПО Расписание.День = &День
    |    И Расписание.Час = ВТ.Час
    |    И (Расписание.Аудитория = ВТ.Аудитория ИЛИ Расписание.Преподаватель = ВТ.Преподаватель)
    |    И Расписание.Регистратор <> &Ссылка";
    
    Запрос.УстановитьПараметр("ТаблицаЗанятий", ТаблицаЗанятий);
    Запрос.УстановитьПараметр("День", День);
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    
    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        Выборка = Результат.Выбрать();
        Пока Выборка.Следующий() Цикл
            // Сообщаем пользователю о конкретной проблеме
            ТекстСообщения = "Ошибка в строке " + Выборка.НомерСтроки + ": ";
            Если ЗначениеЗаполнено(Выборка.ЗанятаяАудитория) Тогда
                ТекстСообщения = ТекстСообщения + "Аудитория уже занята! ";
            КонецЕсли;
            // Аналогично для преподавателя...
            
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = ТекстСообщения;
            Сообщение.Сообщить();
            Отказ = Истина;
        КонецЦикла;
    КонецЕсли;
    
    Если Отказ Тогда Возврат; КонецЕсли;
    
    // 4. Запись движений
    Движения.РасписаниеЗанятий.Записывать = Истина;
    // ... заполнение набора записей из ТЧ ...
    
КонецПроцедуры

Архитектурные советы по проектированию расписания

Проанализируем рекомендации опытных разработчиков из обсуждения. Часто эффективнее «плясать» от Аудитории. Аудитория — это ограниченный ресурс. В одно время в одном месте может находиться только одна группа и один преподаватель.

Важные моменты проектирования:

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

← На главную