Moodle Course & Enrolment Sync
When you create a course program in OEC and assign subjects to it, the integration automatically provisions a 4-level hierarchy in Moodle: department category → program sub-category → one course per subject. When students are enrolled in OEC, they are enrolled in the corresponding Moodle courses. Your Moodle admin does not need to touch anything.
OEC is authoritative for this data. Changes made directly in Moodle's course catalog (title, description, dates) will be overwritten on the next push sync. All structural changes go through OEC.
4-Level Academic Hierarchy
OEC's academic structure maps to Moodle's course hierarchy. Each level must be synced before the level below it, the cron enforces this dependency order.
Department
op.department→Course Categoryoec_dept_{id}e.g. oec_dept_42Topologically sorted, parent departments always synced before children.
Program
op.course→Course Sub-Categoryoec_course_{id}e.g. oec_course_7Placed under the department's Moodle category. Skipped if department not yet mapped.
Subject
op.subject→Moodle Courseoec_subj_{subj_id}_course_{course_id}e.g. oec_subj_15_course_7One Moodle course per (subject, program) pair. Shortname = {subject.code}_{course.code}.
Batch
op.batch→Moodle Group (within course)oec_batch_{id}e.g. oec_batch_3Groups are course-scoped in Moodle. Created in each course corresponding to the batch's program.
The idnumber Cross-Reference Key
Every entity synced to Moodle carries a deterministic idnumber that encodes the OEC database ID. This idnumber is set in Moodle's own idnumber field on courses and categories, and also stored in moodle.mapping.moodle_idnumber in OEC.
This design means the mapping table can be rebuilt from scratch by querying Moodle for records by idnumber, no custom Moodle database columns, no plugins needed. If the mapping table is ever lost, the Initial Import Wizard reads idnumbers from Moodle and reconstructs all mappings.
Subject-as-Course design decision
Each OEC subject maps to a separate Moodle course, not a section or group within a course. A subject shared across three programs creates three Moodle courses, each with a distinct idnumber encoding both the subject ID and the program ID (e.g., oec_subj_15_course_7, oec_subj_15_course_12).
This was a deliberate design choice: each Moodle course has an independent gradebook, so grade mapping is unambiguous, one Moodle course ID maps to exactly one OEC subject in one program context. The alternative (subject-as-section) would require pulling section-level grades, which Moodle's API does not cleanly expose.
What Triggers a Sync
Syncs are triggered both in real-time (on record save) and by the hourly cron.
| Action in OEC | Trigger | Moodle result |
|---|---|---|
| New subject added to a program in OEC | op.course.write() on subject_ids change | SyncSubjects job enqueued → Moodle course created |
| Subject name or code updated in OEC | op.subject.write() on name/code/department_id | SyncSubjects update job enqueued → Moodle course shortname/fullname updated |
| Student enrolled in a course in OEC | Hourly cron (SyncEnrollment) | Student enrolled in all Moodle courses for their subject_ids |
| Student withdraws from a course in OEC | Manual unenrol action or cron | enrol_manual_unenrol_users called; op.student.course.enrol mappings removed |
| Faculty assignment updated in OEC | op.faculty.write() on faculty_subject_ids | Faculty enrolled as teacher in all relevant Moodle courses |
| Full push cron | Hourly _cron_full_push_sync | All entities synced in dependency order (dept → prog → subj → users → batches → enrolments) |
Enrolment Sync: Critical Detail
The SyncEnrollment service uses enrollment.subject_ids, the specific subjects a student has chosen within their enrollment, not course.subject_ids (all subjects in the program).
This means a student enrolled in a program but taking only 4 of 6 available subjects is enrolled in exactly those 4 Moodle courses, not all 6. If a student's subject_ids is empty (e.g., a new enrollment where subject selection is pending), the service falls back to course.subject_ids.
The enrolment call uses enrol_manual_enrol_users and is idempotent, the service checks for an existing op.student.course.enrol mapping before calling the API. Calling enrol twice for the same student-course pair is safe in Moodle but the mapping check prevents the unnecessary API call.
Course Sync, Frequently Asked Questions
Technical questions about course and enrolment provisioning.
Ready to Transform Your Course Sync?
See how OpenEduCat frees up time so every student gets the attention they deserve.
Try it free for 15 days. No credit card required.