Development of a fitness club website using 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Fitness Club Website Development on 1C-Bitrix

A fitness club website is not just a showcase with photos of workout spaces. It's a working tool where a member books a training session, purchases a membership, tracks remaining classes, and communicates with trainers. All of this must function automatically without manual administrator intervention, sync with the club's internal systems, and handle peak loads on Monday evenings when half the city decides to start a new life.

Let's examine how this is implemented on 1C-Bitrix — from data structure to booking logic.

Class Schedule: Highload Blocks and Filtering

The schedule is the central element of the site. Members visit it most frequently, and if the schedule is slow or inconvenient to filter — they'll switch to a competitor's Telegram bot.

Why Highload Block Instead of a Standard Infoblock. A standard infoblock (b_iblock_element) stores data using an EAV model: each property is a separate row in b_iblock_element_property. With 300 classes per week, 15 workout spaces, and 40 trainers, the properties table grows to tens of thousands of rows. Filtering by the combination of "space + day + trainer + training type" becomes a series of JOINs that on a production server take 800-1200 ms.

A Highload Block (b_hlblock_entity) is a flat table in MySQL/PostgreSQL. One row equals one class, all fields are columns. Filtering works through standard indexes.

Highload Block FitnessSchedule Structure:

Field Type Purpose
UF_DATE date Class date
UF_TIME_START string Start time (HH:MM format)
UF_TIME_END string End time
UF_HALL_ID integer Workout space ID (link to HL FitnessHalls)
UF_TRAINER_ID integer Trainer ID (link to trainers infoblock)
UF_DIRECTION_ID integer Training direction: yoga, crossfit, swimming pool...
UF_CAPACITY integer Maximum number of participants
UF_BOOKED integer Current number of registered members
UF_STATUS enumeration active / cancelled / full
UF_IS_RECURRING boolean Recurring by template
UF_TEMPLATE_ID integer Link to schedule template

For recurring classes, a separate Highload Block ScheduleTemplate is created with fields for day of week and time. A cron job (agents in Bitrix or system cron) generates specific classes for the next week from templates once a week. This allows a trainer to cancel a specific class on March 15 without breaking the entire schedule.

Frontend Filtering. We use Bitrix\Highloadblock\HighloadBlockTable::compileEntity() to get the ORM entity, then standard DataManager::getList() with a filter:

$result = $entityClass::getList([
    'filter' => [
        'UF_DATE' => $selectedDate,
        'UF_HALL_ID' => $hallId,
        'UF_STATUS' => 'active',
    ],
    'order' => ['UF_TIME_START' => 'ASC'],
]);

On the frontend, the schedule is rendered as a grid: horizontally — workout spaces, vertically — time slots. The member switches days, filters by training type or trainer. AJAX requests work through the bitrix:highloadblock.list component or a custom REST endpoint.

Online Booking with Capacity Limits and Waitlist

Booking a class isn't just "clicking a button and getting on the list." It's a transactional operation with capacity checking, concurrent access control, and a waiting mechanism.

Main Scenario:

  1. Member clicks "Book" on a specific class
  2. System checks: UF_BOOKED < UF_CAPACITY
  3. If true — creates a booking record in the Highload Block FitnessBooking, increments UF_BOOKED
  4. If false — offers to join the waitlist

Concurrent Access Problem. Two members simultaneously click "Book" on a class with one remaining spot. Without locking, both get confirmation, but the trainer has an extra person.

The solution is using \Bitrix\Main\Application::getConnection() with transaction and row locking:

$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();

$row = $entityClass::getList([
    'filter' => ['ID' => $scheduleId],
    'select' => ['UF_BOOKED', 'UF_CAPACITY'],
    'runtime' => [/* FOR UPDATE via raw SQL */],
])->fetch();

if ($row['UF_BOOKED'] < $row['UF_CAPACITY']) {
    // create booking, increment UF_BOOKED
    $connection->commitTransaction();
} else {
    $connection->rollbackTransaction();
    // offer waitlist
}

In practice, Bitrix's pure ORM doesn't support SELECT ... FOR UPDATE, so the critical section is wrapped in raw SQL through $connection->query().

Waitlist. A separate Highload Block FitnessWaitlist with fields: UF_SCHEDULE_ID, UF_USER_ID, UF_POSITION, UF_CREATED_AT. When someone cancels a booking, a Bitrix agent checks the waitlist and automatically moves the first person in queue to the main list, sending SMS/push through the messageservice module.

Booking Cancellation. Clubs typically allow cancellation 2-4 hours before start time. The time check logic is in the event handler, which compares UF_TIME_START with current time and blocks cancellation if the deadline has passed.

Membership Sales Through the Sale Module

Club memberships aren't catalog products. They have their own logic: validity period, visit limits, freezing.

Membership Types are implemented as infoblock elements with properties:

  • DURATION_DAYS — validity period in days
  • VISIT_LIMIT — visit limit (0 = unlimited)
  • TYPE — single / monthly / annual
  • FREEZE_ALLOWED — can be frozen
  • FREEZE_MAX_DAYS — maximum freeze duration

When purchased through \Bitrix\Sale\Order::create(), the membership is added to the cart as a regular product, but after payment, the OnSaleOrderPaid event handler triggers. It creates a record in the Highload Block UserSubscription with fields: UF_USER_ID, UF_START_DATE, UF_END_DATE, UF_VISITS_LEFT, UF_IS_FROZEN, UF_FREEZE_START.

Membership Freezing. A member clicks "Freeze" in their account. The system checks FREEZE_ALLOWED and FREEZE_MAX_DAYS, sets UF_IS_FROZEN = true, saves UF_FREEZE_START. On unfreezing — recalculates UF_END_DATE by adding the frozen days.

Integration with Club CRM Systems

Fitness clubs rarely operate only through a website. Main accounting systems are 1C:Fitness Club and Mobifitness.

1C:Fitness Club. Exchange through the standard CommerceML protocol or through 1C's REST API (HTTP services). Synchronized: service directory, schedule, client database, membership sales. Exchange runs by cron every 15-30 minutes.

Mobifitness API. REST API with token authorization. Main endpoints: /api/v1/schedule, /api/v1/bookings, /api/v1/clients. Bitrix acts as the frontend, Mobifitness — as the master schedule system. In this case, the Highload Block FitnessSchedule is filled not manually, but through API sync.

The architecture choice depends on which system is the master data source. If the club is already using Mobifitness — the site pulls the schedule from there. If the club is going digital from scratch — Bitrix can be the main system.

Member Account

The member account is built on the standard main module (authentication, profile) with extensions:

  • Visit History — query from FitnessBooking by UF_USER_ID with JOIN to schedule
  • Remaining Classes — field UF_VISITS_LEFT from UserSubscription
  • Membership Renewal — button that creates an order in sale linked to current subscription
  • Freeze/Unfreeze — interface for managing subscription status

Authentication is through phone with SMS code (messageservice module + custom component). Fitness audience doesn't like passwords.

Trainer Profiles

Trainers are a separate infoblock with links to training types through a multiple "Element Link" property. Each trainer has: photo, certificates (file properties), experience, description, link to their schedule (filter by UF_TRAINER_ID).

On the trainer's detail page, their schedule for the current week is automatically displayed — one AJAX request to FitnessSchedule filtered by trainer.

Implementation Timeline

Project Scale Scope Timeline
Small club (1 space, 5-7 training types) Schedule, booking, memberships, member account 8-10 weeks
Network of 3-5 clubs + multisite, unified database, Mobifitness integration 14-18 weeks
Large network (10+ clubs) + B2B portal for corporate clients, mobile app via Bitrix REST API, complex pricing 20-28 weeks

Before Starting

Before development, determine the master system for schedules: will the schedule be maintained in Bitrix or in a third-party CRM? This determines synchronization direction and Highload Block architecture. The second question is the payment gateway: for memberships with recurring billing, you need acquiring that supports recurring payments, which is a separate integration through sale.paysystem.