Как организовать проверку занятых мест в автобусе через регистр сведений

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

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

Проектирование структуры регистра сведений

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

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

  1. Рейс (Тип: ДокументСсылка.Рейс или СправочникСсылка.Рейсы) — это ключевое измерение. Без привязки к конкретному рейсу (дате и времени) мы не сможем отличить поездку, которая идет сегодня, от той, что будет завтра.
  2. Место (Тип: Число или СправочникСсылка.Места) — номер конкретного кресла в автобусе.

Что касается ресурсов, рассмотрим два подхода:

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

Реализация проверки занятости через запрос

Теперь рассмотрим, как написать запрос, который будет проверять, свободно ли место перед тем, как записать в систему новый билет. Представим, что пользователь выбирает номер места в документе. Нам нужно убедиться, что в базе еще нет записи с таким же сочетанием рейса и места.

Проанализируем пример кода запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	МестаВАвтобусе.Пассажир КАК Пассажир
    |ИЗ
    |	РегистрСведений.ЗанятыеМеста КАК МестаВАвтобусе
    |ГДЕ
    |	МестаВАвтобусе.Рейс = &Рейс
    |	И МестаВАвтобусе.Место = &Место";

Запрос.УстановитьПараметр("Рейс", СсылкаНаРейс);
Запрос.УстановитьПараметр("Место", ВыбранноеМесто);

РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
    Сообщить("Извините, это место уже занято другим пассажиром!");
КонецЕсли;

В этом примере мы используем метод Пустой(). Если запрос вернул хоть одну строку, значит, в регистре уже есть запись, и бронирование запрещено. Рассмотрим подробнее: такой подход идеален для проверки одного конкретного места.

Массовая проверка мест при проведении документа

Если наш документ предполагает продажу сразу нескольких билетов (например, для группы пассажиров), проверять каждое место в цикле — плохая практика. Выясним причину: это создает лишнюю нагрузку на сервер базы данных. Вместо этого проанализируем ситуацию и используем временные таблицы.

Рассмотрим алгоритм массовой проверки:

  1. Выгружаем таблицу выбранных мест из документа во временную таблицу запроса.
  2. Соединяем её с таблицей регистра сведений по полям Рейс и Место.
  3. Если в результате соединения поле из регистра НЕ ЕСТЬ NULL, значит, это место уже занято.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВыбранныеМеста.НомерМеста КАК НомерМеста
    |ПОМЕСТИТЬ ВТ_НовыеМеста
    |ИЗ
    |	&ТЧ_Места КАК ВыбранныеМеста
    |;
    |
    |ВЫБРАТЬ
    |	ВТ_НовыеМеста.НомерМеста КАК ЗанятоеМесто
    |ИЗ
    |	ВТ_НовыеМеста КАК ВТ_НовыеМеста
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗанятыеМеста КАК Регистр
    |		ПО ВТ_НовыеМеста.НомерМеста = Регистр.Место
    |ГДЕ
    |	Регистр.Рейс = &Рейс";

Запрос.УстановитьПараметр("ТЧ_Места", ТабличнаяЧастьМеста.Выгрузить());
Запрос.УстановитьПараметр("Рейс", Объект.Рейс);

Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        ТекстОшибки = "Место №" + Выборка.ЗанятоеМесто + " уже забронировано!";
        // Вывод сообщения пользователю
    КонецЦикла;
КонецЕсли;

Важность управляемых блокировок

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

Чтобы этого избежать, мы должны использовать объект БлокировкаДанных. Перед выполнением запроса на проверку необходимо заблокировать пространство измерений регистра:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЗанятыеМеста");
ЭлементБлокировки.УстановитьПараметр("Рейс", Объект.Рейс);
ЭлементБлокировки.УстановитьПараметр("Место", ВыбранноеМесто);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();

// И только теперь выполняем запрос и запись...

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

Визуализация: закрашивание занятых мест

Посмотрим на практическую сторону вопроса — как сделать интерфейс удобным. Чтобы "закрашивать зеленым" или "красным" квадраты мест на форме, используем Условное оформление.

Разберем по шагам:

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

Если используется графическая схема (например, через картинки), мы можем использовать метод Обновить() для формы и в обработчике ожидания проверять актуальность данных, чтобы оператор видел изменения, сделанные коллегами, почти мгновенно.

Выбор между Регистром Сведений и Регистром Накопления

В ходе обсуждения часто возникает вопрос: почему не использовать РегистрНакопления? Разберем этот момент подробнее.

Регистры накопления (остатки) отлично подходят для количественного учета: "в автобусе всего 50 мест, осталось 10". Но они плохо справляются с задачей "занять конкретное место №14". В регистре накопления вам пришлось бы делать каждое место отдельным значением измерения, что архитектурно неверно.

Регистр Сведений позволяет хранить статус каждой конкретной точки (места) и дополнительные сведения (кто купил, когда, какой статус оплаты). Поэтому для задачи "посадочного места" РегистрСведений является наиболее подходящим инструментом.

Заключение

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

← На главную