В процессе разработки систем бронирования, будь то билеты на автобус, места в кинотеатре или номера в отеле, перед программистом 1С часто встает задача: как эффективно хранить данные о занятых ресурсах и проверять их доступность в реальном времени. В данной статье мы вместе разберем, как спроектировать независимый регистр сведений для учета мест в автобусе, напишем запрос для проверки их занятости и обсудим профессиональные тонкости, такие как конкурентный доступ и визуализация схемы рассадки.
Первым шагом в решении нашей задачи является определение правильной структуры РегистрСведений. Важно понимать, что регистр должен обеспечивать уникальность записи. Если мы создадим регистр только с измерением Место, то сможем записать данные об одном автобусе только один раз. Проанализируем, какие измерения нам необходимы.
Для корректной работы системы нам понадобятся следующие измерения:
ДокументСсылка.Рейс или СправочникСсылка.Рейсы) — это ключевое измерение. Без привязки к конкретному рейсу (дате и времени) мы не сможем отличить поездку, которая идет сегодня, от той, что будет завтра.Число или СправочникСсылка.Места) — номер конкретного кресла в автобусе.Что касается ресурсов, рассмотрим два подхода:
Занято (Тип: Булево). Это упрощает запрос: если истина — место занято.Пассажир (Тип: СправочникСсылка.ФизическиеЛица). Здесь логика проста: если ресурс заполнен, значит, место занято. Это избавляет от избыточности, так как наличие пассажира само по себе является признаком занятости.Разберем ситуацию, когда автобус может меняться. Если на один и тот же рейс в разные дни могут назначаться разные автобусы, то Автобус лучше вынести в реквизиты регистра или получать его непосредственно из документа рейса, чтобы не усложнять индексную структуру регистра.
Теперь рассмотрим, как написать запрос, который будет проверять, свободно ли место перед тем, как записать в систему новый билет. Представим, что пользователь выбирает номер места в документе. Нам нужно убедиться, что в базе еще нет записи с таким же сочетанием рейса и места.
Проанализируем пример кода запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МестаВАвтобусе.Пассажир КАК Пассажир
|ИЗ
| РегистрСведений.ЗанятыеМеста КАК МестаВАвтобусе
|ГДЕ
| МестаВАвтобусе.Рейс = &Рейс
| И МестаВАвтобусе.Место = &Место";
Запрос.УстановитьПараметр("Рейс", СсылкаНаРейс);
Запрос.УстановитьПараметр("Место", ВыбранноеМесто);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Сообщить("Извините, это место уже занято другим пассажиром!");
КонецЕсли;
В этом примере мы используем метод Пустой(). Если запрос вернул хоть одну строку, значит, в регистре уже есть запись, и бронирование запрещено. Рассмотрим подробнее: такой подход идеален для проверки одного конкретного места.
Если наш документ предполагает продажу сразу нескольких билетов (например, для группы пассажиров), проверять каждое место в цикле — плохая практика. Выясним причину: это создает лишнюю нагрузку на сервер базы данных. Вместо этого проанализируем ситуацию и используем временные таблицы.
Рассмотрим алгоритм массовой проверки:
Рейс и Место.НЕ ЕСТЬ NULL, значит, это место уже занято.Пример такого запроса для обработки проведения документа:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВыбранныеМеста.НомерМеста КАК НомерМеста
|ПОМЕСТИТЬ ВТ_НовыеМеста
|ИЗ
| &ТЧ_Места КАК ВыбранныеМеста
|;
|
|ВЫБРАТЬ
| ВТ_НовыеМеста.НомерМеста КАК ЗанятоеМесто
|ИЗ
| ВТ_НовыеМеста КАК ВТ_НовыеМеста
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗанятыеМеста КАК Регистр
| ПО ВТ_НовыеМеста.НомерМеста = Регистр.Место
|ГДЕ
| Регистр.Рейс = &Рейс";
Запрос.УстановитьПараметр("ТЧ_Места", ТабличнаяЧастьМеста.Выгрузить());
Запрос.УстановитьПараметр("Рейс", Объект.Рейс);
Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
ТекстОшибки = "Место №" + Выборка.ЗанятоеМесто + " уже забронировано!";
// Вывод сообщения пользователю
КонецЦикла;
КонецЕсли;
Рассмотрим критически важный момент, который часто упускают новички. В многопользовательской среде два оператора могут одновременно нажать кнопку "Забронировать". Оба запроса на проверку выполнятся одновременно, оба увидят, что место свободно, и оба запишут данные. В итоге мы получим два проданных билета на одно место.
Чтобы этого избежать, мы должны использовать объект БлокировкаДанных. Перед выполнением запроса на проверку необходимо заблокировать пространство измерений регистра:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЗанятыеМеста");
ЭлементБлокировки.УстановитьПараметр("Рейс", Объект.Рейс);
ЭлементБлокировки.УстановитьПараметр("Место", ВыбранноеМесто);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// И только теперь выполняем запрос и запись...
Важно: Использование исключительной блокировки гарантирует, что пока одна транзакция не завершится, вторая не сможет даже прочитать эти данные для проверки, что исключает "двойное бронирование".
Посмотрим на практическую сторону вопроса — как сделать интерфейс удобным. Чтобы "закрашивать зеленым" или "красным" квадраты мест на форме, используем Условное оформление.
Разберем по шагам:
ПриСозданииНаСервере или при изменении рейса будем получать список всех занятых мест для этого рейса и помещать их в массив или структуру на форме.Статус (которое мы программно вычисляем) равно "Занято", то цвет фона ячейки становится красным.Если используется графическая схема (например, через картинки), мы можем использовать метод Обновить() для формы и в обработчике ожидания проверять актуальность данных, чтобы оператор видел изменения, сделанные коллегами, почти мгновенно.
В ходе обсуждения часто возникает вопрос: почему не использовать РегистрНакопления? Разберем этот момент подробнее.
Регистры накопления (остатки) отлично подходят для количественного учета: "в автобусе всего 50 мест, осталось 10". Но они плохо справляются с задачей "занять конкретное место №14". В регистре накопления вам пришлось бы делать каждое место отдельным значением измерения, что архитектурно неверно.
Регистр Сведений позволяет хранить статус каждой конкретной точки (места) и дополнительные сведения (кто купил, когда, какой статус оплаты). Поэтому для задачи "посадочного места" РегистрСведений является наиболее подходящим инструментом.
Мы проанализировали процесс создания системы контроля мест и выяснили, что ключ к успеху лежит в правильном проектировании измерений регистра (обязательно добавляем Рейс) и грамотной работе с запросами. Не забывайте про блокировки данных и старайтесь переносить тяжелые проверки на сторону сервера. Надеемся, что эти советы помогут вам создать надежный и быстрый механизм бронирования в ваших учебных и рабочих проектах.