LMS Development on 1C-Bitrix: Building an Educational Platform

When training partners or employees becomes a necessity, and a separate LMS means an extra admin panel and complex synchronization, we offer a solution integrated into your 1C-Bitrix. Our team builds an educational platform right inside your ecosystem, uniting users, orders, and payments in a single space. We deliver turnkey projects—from concept to ongoing support—ensuring reliable operation and continued assistance.

Our competencies:

Frequently Asked Questions

LMS Development on 1C-Bitrix: Building an Educational Platform

You already use 1C-Bitrix for your store or corporate portal, and now you need to launch partner training. A separate LMS means another admin panel, another login, and synchronization headaches. We solve it differently: we build the platform right inside your Bitrix. Single user database, shared orders, payment module—everything works out of the box. In 10+ years we have implemented 7 educational projects on Bitrix and know all the pitfalls.

A typical situation: a client already sells products through an online store on Bitrix and now wants to launch online courses. Implementing a separate LMS (e.g., Moodle) would require duplicating users, setting up SSO, and migrating data. All this can be solved with standard Bitrix tools—infoblocks, HL-blocks, and user groups. Time savings on integration—up to 40% compared to external solutions.

Why Bitrix Is Suitable for an LMS

Bitrix provides infoblocks for storing courses and lessons, user groups for access, agents for background tasks, and built-in payment systems. A ready-made LMS on Bitrix costs 2–3 times less than implementing a separate Moodle if the business already uses the 1C-Bitrix ecosystem. Everything is managed from a single admin panel, and for students—a unified personal account. Additionally, we use tagged caching for course pages: when content changes, only the cache of the specific lesson is cleared, not the entire section.

Data Storage Architecture

The LMS on Bitrix is built on a combination of infoblocks and HighLoad-blocks (HL-blocks, tables via ORM D7).

Infoblocks:

Infoblock Code Purpose
Courses lms_courses Main course entities
Lessons lms_lessons Lessons within a course
Quizzes lms_quizzes Assignments and tests

HL-blocks (via ORM):

// User progress
class UserProgressTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string
    {
        return 'b_hl_lms_user_progress';
    }

    public static function getMap(): array
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new IntegerField('USER_ID'),
            new IntegerField('COURSE_ID'), // ID of course infoblock element
            new IntegerField('LESSON_ID'), // ID of lesson infoblock element
            new EnumField('STATUS', ['values' => ['NOT_STARTED', 'IN_PROGRESS', 'COMPLETED']]),
            new IntegerField('PROGRESS_PERCENT'), // 0–100
            new DatetimeField('STARTED_AT'),
            new DatetimeField('COMPLETED_AT'),
            new IntegerField('TIME_SPENT_SEC'), // Time spent on lesson
        ];
    }
}

// Course enrollments
class CourseEnrollmentTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string
    {
        return 'b_hl_lms_enrollment';
    }

    public static function getMap(): array
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new IntegerField('USER_ID'),
            new IntegerField('COURSE_ID'),
            new EnumField('STATUS', ['values' => ['ACTIVE', 'COMPLETED', 'EXPIRED', 'REFUNDED']]),
            new DatetimeField('ENROLLED_AT'),
            new DatetimeField('EXPIRES_AT'), // NULL = unlimited
            new FloatField('PAID_AMOUNT'),
            new IntegerField('ORDER_ID'), // Link to order from sale module
        ];
    }
}

Course and Lesson Structure

A course is an infoblock element lms_courses with properties:

Property Code Type
Category CATEGORY List
Duration DURATION_HOURS Number
Level LEVEL List (Beginner/Intermediate/Expert)
Access type ACCESS_TYPE List (Free/Paid/Subscription)
Access group ACCESS_GROUP_ID Number (group ID b_group)
Certificate HAS_CERTIFICATE Flag

A lesson is an infoblock element lms_lessons, linked to a course via property COURSE_ID. Lesson order via SORT. Lesson content type (LESSON_TYPE): Video / Text / Test / Webinar.

How Is Course Access Managed?

Access is controlled via Bitrix user groups. Each course has its own group (b_group). After payment or enrollment, the user is added to the group:

class EnrollmentService {
    public function enroll(int $userId, int $courseId): void {
        $course = \CIBlockElement::GetByID($courseId)->GetNext();
        $groupId = (int)$course['PROPERTY_ACCESS_GROUP_ID_VALUE'];
        if ($groupId) {
            $currentGroups = \CUser::GetUserGroup($userId);
            if (!in_array($groupId, $currentGroups)) {
                $currentGroups[] = $groupId;
                \CUser::SetUserGroup($userId, $currentGroups);
            }
        }
        CourseEnrollmentTable::add([
            'USER_ID' => $userId,
            'COURSE_ID' => $courseId,
            'STATUS' => 'ACTIVE',
            'ENROLLED_AT' => new \Bitrix\Main\Type\DateTime(),
        ]);
    }
}

Access to course pages is checked similarly: if no enrollment record exists or status is not ACTIVE—redirect to the purchase page.

Progress Tracking

Progress is recorded under several conditions depending on lesson type:

  • Video: tracking via player timeupdate event, marked as watched at 80% video.
  • Text: marked when scrolled to the end (IntersectionObserver on last paragraph).
  • Test: on successful passing (score ≥ pass threshold).
// AJAX endpoint for progress saving
// POST /local/ajax/lms_progress.php
$lessonId = (int)$_POST['lesson_id'];
$courseId = (int)$_POST['course_id'];
$userId = $USER->GetID();
$percent = min(100, (int)$_POST['percent']);

$existing = UserProgressTable::getList([
    'filter' => ['USER_ID' => $userId, 'LESSON_ID' => $lessonId],
])->fetch();

if ($existing) {
    if ($percent > $existing['PROGRESS_PERCENT']) {
        UserProgressTable::update($existing['ID'], [
            'PROGRESS_PERCENT' => $percent,
            'STATUS' => $percent >= 100 ? 'COMPLETED' : 'IN_PROGRESS',
            'COMPLETED_AT' => $percent >= 100 ? new \Bitrix\Main\Type\DateTime() : null,
        ]);
    }
} else {
    UserProgressTable::add([
        'USER_ID' => $userId,
        'COURSE_ID' => $courseId,
        'LESSON_ID' => $lessonId,
        'STATUS' => $percent >= 100 ? 'COMPLETED' : 'IN_PROGRESS',
        'PROGRESS_PERCENT' => $percent,
        'STARTED_AT' => new \Bitrix\Main\Type\DateTime(),
        'COMPLETED_AT' => $percent >= 100 ? new \Bitrix\Main\Type\DateTime() : null,
    ]);
}

Testing and Quizzes

Test questions are infoblock elements lms_quizzes with properties: question type (single choice, multiple choice, text answer), answer options (JSON in string property), correct answer, question weight.

Test results—HL-block b_hl_lms_quiz_attempt:

CREATE TABLE b_hl_lms_quiz_attempt (
    ID SERIAL PRIMARY KEY,
    USER_ID INTEGER,
    QUIZ_ID INTEGER,
    SCORE INTEGER, -- points earned
    MAX_SCORE INTEGER, -- maximum
    PASSED BOOLEAN,
    ANSWERS_JSON TEXT, -- JSON with user answers
    CREATED_AT TIMESTAMP
);

Certificates

Upon course completion (all lessons COMPLETED)—generate a PDF certificate using the FPDF or TCPDF library:

class CertificateGenerator {
    public function generate(int $userId, int $courseId): string {
        $user = \CUser::GetByID($userId)->Fetch();
        $course = \CIBlockElement::GetByID($courseId)->GetNext();
        $pdf = new \TCPDF();
        $pdf->AddPage('L'); // Landscape
        $pdf->setImageScale(1.25);
        // Certificate background image
        $pdf->Image('/local/templates/lms/img/certificate_bg.jpg', 0, 0, 297, 210);
        // Full name
        $pdf->SetFont('dejavusans', 'B', 32);
        $pdf->SetXY(50, 90);
        $pdf->Cell(200, 20, $user['LAST_NAME'] . ' ' . $user['NAME'], 0, 0, 'C');
        // Course name
        $pdf->SetFont('dejavusans', '', 18);
        $pdf->SetXY(50, 120);
        $pdf->Cell(200, 10, $course['NAME'], 0, 0, 'C');
        $filename = 'certificate_' . $userId . '_' . $courseId . '.pdf';
        $path = '/upload/certificates/' . $filename;
        $pdf->Output($_SERVER['DOCUMENT_ROOT'] . $path, 'F');
        return $path;
    }
}

Process of Work

  1. Analytics and design — agree on course structure, content types, access scheme.
  2. Component development — create 2.0 components for course list, detail page, progress.
  3. Integration — connect payment systems (YooKassa, Sber) and exchange with 1C via CommerceML.
  4. Testing — verify scenarios of purchase, enrollment, lesson completion, and certificate generation.
  5. Deployment and training — roll out to production server, train administrators (2–3 hours).

What Is Included in the Work

When ordering a turnkey LMS development, we deliver:

  • analytics and data schema design (infoblocks, HL-blocks);
  • development of 2.0 components (course list, detail, progress);
  • integration with payment systems and 1C;
  • testing and bug fixing;
  • delivery of source code, documentation, and accesses;
  • administrator training.

Development Timeline

Variant Scope Duration
Basic LMS Courses, lessons, progress, group access 15–20 days
With tests and certificates + Quizzes, PDF generation 20–30 days
Full platform + Payments, subscriptions, analytics, webinars 35–50 days

Want to discuss your project? Contact us—we'll evaluate your task and propose an optimal solution. Order an LMS development—and get a ready-made solution within agreed deadlines.