Налаштування онлайн-запису до лікаря на 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Показано 1 з 1 послугУсі 1626 послуг
Налаштування онлайн-запису до лікаря на 1С-Бітрікс
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування онлайн-запису до лікаря на 1С-Бітрікс

Сайт медичної клініки з формою «Залиште заявку і ми передзвонимо» втрачає значну частку конверсії — користувач хоче обрати конкретного лікаря, конкретний час і отримати підтвердження негайно. Онлайн-запис із вибором слоту — стандарт для медицини. Реалізація на 1С-Бітрікс передбачає зв'язок з МІС або управління розкладом усередині самого 1С-Бітрікс, якщо МІС немає.

Джерело розкладу

Перше питання при проектуванні: звідки береться розклад?

Варіант A: Розклад у 1С-Бітрікс. Адміністратор клініки управляє розкладом лікарів через інтерфейс у 1С-Бітрікс. Записи зберігаються в 1С-Бітрікс і передаються в МІС (або не передаються — клініка без МІС). Підходить для невеликих клінік без складної МІС.

Варіант B: Розклад з МІС. 1С-Бітрікс синхронізує розклад із МІС кожні N хвилин. Записи створюються через API МІС. Сайт — лише інтерфейс, майстер-дані в МІС.

Описуємо Варіант A — автономний розклад у 1С-Бітрікс.

Таблиці розкладу

-- Шаблон робочого часу лікаря
CREATE TABLE local_doctor_schedule_template (
    ID         INT AUTO_INCREMENT PRIMARY KEY,
    DOCTOR_ID  INT NOT NULL,         -- ID елемента інфоблоку «Лікарі»
    DAY_OF_WEEK TINYINT NOT NULL,    -- 1=Пн, 7=Нд
    TIME_FROM  TIME NOT NULL,
    TIME_TO    TIME NOT NULL,
    SLOT_DURATION INT DEFAULT 30,   -- хвилин на прийом
    ACTIVE     CHAR(1) DEFAULT 'Y'
);

-- Конкретні слоти (генеруються із шаблону)
CREATE TABLE local_doctor_slots (
    ID          BIGINT AUTO_INCREMENT PRIMARY KEY,
    DOCTOR_ID   INT NOT NULL,
    SLOT_DATE   DATE NOT NULL,
    SLOT_TIME   TIME NOT NULL,
    STATUS      ENUM('free','reserved','booked','blocked') DEFAULT 'free',
    APPOINTMENT_ID BIGINT,
    INDEX idx_doctor_date (DOCTOR_ID, SLOT_DATE, STATUS)
);

-- Записи пацієнтів
CREATE TABLE local_appointments (
    ID          BIGINT AUTO_INCREMENT PRIMARY KEY,
    DOCTOR_ID   INT NOT NULL,
    SLOT_ID     BIGINT NOT NULL,
    USER_ID     INT,                 -- NULL для незареєстрованих
    PATIENT_NAME VARCHAR(200),
    PATIENT_PHONE VARCHAR(20),
    PATIENT_EMAIL VARCHAR(200),
    SERVICE_ID  INT,                 -- Послуга (інфоблок послуг)
    COMMENT     TEXT,
    STATUS      ENUM('pending','confirmed','cancelled','completed') DEFAULT 'pending',
    CREATED_AT  DATETIME,
    CONFIRMED_AT DATETIME,
    CANCELLED_AT DATETIME
);

Генерація слотів із шаблону

Агент, що запускається щодня, генерує слоти на наступні 30 днів:

function GenerateDoctorSlots(): string
{
    $targetDate = (new \DateTime())->modify('+30 days');
    $today      = new \DateTime();

    $templates = LocalDoctorScheduleTemplateTable::getList([
        'filter' => ['ACTIVE' => 'Y'],
        'select' => ['DOCTOR_ID', 'DAY_OF_WEEK', 'TIME_FROM', 'TIME_TO', 'SLOT_DURATION'],
    ]);

    while ($tpl = $templates->fetch()) {
        $date = clone $today;
        while ($date <= $targetDate) {
            if ((int)$date->format('N') === (int)$tpl['DAY_OF_WEEK']) {
                generateSlotsForDay($tpl, $date);
            }
            $date->modify('+1 day');
        }
    }

    return __FUNCTION__ . '();';
}

function generateSlotsForDay(array $tpl, \DateTime $date): void
{
    $from     = new \DateTime($date->format('Y-m-d') . ' ' . $tpl['TIME_FROM']);
    $to       = new \DateTime($date->format('Y-m-d') . ' ' . $tpl['TIME_TO']);
    $interval = new \DateInterval('PT' . $tpl['SLOT_DURATION'] . 'M');

    $current = clone $from;
    while ($current < $to) {
        // Не створюємо дублі
        $exists = LocalDoctorSlotsTable::getCount([
            'DOCTOR_ID' => $tpl['DOCTOR_ID'],
            'SLOT_DATE' => $date->format('Y-m-d'),
            'SLOT_TIME' => $current->format('H:i:s'),
        ]);

        if (!$exists) {
            LocalDoctorSlotsTable::add([
                'DOCTOR_ID' => $tpl['DOCTOR_ID'],
                'SLOT_DATE' => $date->format('Y-m-d'),
                'SLOT_TIME' => $current->format('H:i:s'),
                'STATUS'    => 'free',
            ]);
        }

        $current->add($interval);
    }
}

Компонент вибору слоту

Компонент /local/components/local/appointment.booking/ з кроками:

Крок 1 — Вибір лікаря/спеціалізації. Фільтр за спеціалізацією з інфоблоку лікарів. AJAX-оновлення списку лікарів.

Крок 2 — Вибір дати та часу. Календар із підсвіченими доступними датами. При виборі дати — AJAX-запит доступних слотів:

// AJAX-обробник /local/ajax/get-slots.php
$doctorId  = (int)$_POST['doctor_id'];
$date      = $_POST['date']; // Y-m-d

$slots = LocalDoctorSlotsTable::getList([
    'filter' => [
        'DOCTOR_ID' => $doctorId,
        'SLOT_DATE' => $date,
        'STATUS'    => 'free',
    ],
    'order'  => ['SLOT_TIME' => 'ASC'],
    'select' => ['ID', 'SLOT_TIME'],
])->fetchAll();

header('Content-Type: application/json');
echo json_encode(['slots' => $slots]);

Крок 3 — Форма пацієнта. Ім'я, телефон, email, коментар. Для авторизованих — дані підставляються з профілю. Валідація номера телефону.

Крок 4 — Підтвердження та бронювання.

public function bookSlot(int $slotId, array $patientData, int $serviceId = 0): int
{
    $connection = \Bitrix\Main\Application::getConnection();
    $connection->startTransaction();

    try {
        // Атомарне резервування слоту
        $connection->queryExecute("
            UPDATE local_doctor_slots
            SET STATUS = 'reserved'
            WHERE ID = ? AND STATUS = 'free'
        ", [$slotId]);

        if ($connection->getAffectedRowsCount() === 0) {
            throw new \RuntimeException('Цей слот вже зайнятий');
        }

        $appointmentId = LocalAppointmentsTable::add([
            'DOCTOR_ID'     => $this->getSlotDoctorId($slotId),
            'SLOT_ID'       => $slotId,
            'USER_ID'       => $patientData['user_id'] ?? null,
            'PATIENT_NAME'  => $patientData['name'],
            'PATIENT_PHONE' => $patientData['phone'],
            'PATIENT_EMAIL' => $patientData['email'],
            'SERVICE_ID'    => $serviceId,
            'COMMENT'       => $patientData['comment'] ?? '',
            'STATUS'        => 'confirmed',
        ])->getId();

        // Оновлюємо слот — статус і прив'язка до запису
        LocalDoctorSlotsTable::update($slotId, [
            'STATUS'         => 'booked',
            'APPOINTMENT_ID' => $appointmentId,
        ]);

        $connection->commitTransaction();

        // Сповіщення поза транзакцією
        $this->sendConfirmationSms($patientData['phone'], $appointmentId);
        $this->sendConfirmationEmail($patientData['email'], $appointmentId);

        return $appointmentId;

    } catch (\Exception $e) {
        $connection->rollbackTransaction();
        throw $e;
    }
}

Транзакція з UPDATE ... WHERE STATUS = 'free' та перевіркою affectedRows — захист від race condition при одночасному записі двох користувачів на один слот.

Скасування та перенесення запису з особистого кабінету

Пацієнт може скасувати запис не пізніше ніж за N годин до прийому:

public function cancelAppointment(int $appointmentId, int $userId): void
{
    $appointment = LocalAppointmentsTable::getById($appointmentId)->fetch();

    if (!$appointment || (int)$appointment['USER_ID'] !== $userId) {
        throw new \RuntimeException('Запис не знайдено');
    }

    $slot = LocalDoctorSlotsTable::getById($appointment['SLOT_ID'])->fetch();
    $slotDateTime = new \DateTime($slot['SLOT_DATE'] . ' ' . $slot['SLOT_TIME']);

    if ($slotDateTime <= (new \DateTime())->modify('+2 hours')) {
        throw new \RuntimeException('Скасування запису можливе не пізніше ніж за 2 години');
    }

    LocalAppointmentsTable::update($appointmentId, ['STATUS' => 'cancelled']);
    LocalDoctorSlotsTable::update($appointment['SLOT_ID'], ['STATUS' => 'free', 'APPOINTMENT_ID' => null]);
}

Склад робіт

  • Таблиці шаблону розкладу, слотів, записів
  • Агент генерації слотів
  • Компонент бронювання: вибір лікаря → дата → слот → форма → підтвердження
  • AJAX-обробники для слотів
  • Захист від race condition при одночасному записі
  • SMS/email сповіщення, нагадування
  • Особистий кабінет: історія записів, скасування

Терміни: 3–5 тижнів автономна система без МІС. 6–10 тижнів з інтеграцією МІС.