Technical reference for developers working on the EMS module.
| Model | Description |
|---|---|
| ems.level | Study levels (top of the curriculum hierarchy) |
| ems.study | Study programmes under a level: subjects, curriculum documents, the uses_enrollment_flow derived flag |
| ems.subject | Subjects: the most widely referenced curriculum node, and the automatic product.product sync on create/write |
| ems.outcome | Learning outcomes nested in a subject’s form — no menu of its own; the subject_id dangling-compute bug fix |
| ems.criteria | Evaluation criteria nested two levels deep (subject → outcome popup → criteria popup); the identical dangling-compute bug fix on outcome_id |
| ems.content | Content items with a real self-referencing “Composite” hierarchy; fixes a view-context bug where adding a nested child silently created a sibling instead |
| Model | Description |
|---|---|
| ems.course | Academic year windows; known limitation — no course-management UI exists yet (only the “Current course” selector), see the doc for the unbuilt “Setup next course” TODO |
| res.company (EMS extension) | Every centre-wide config field EMS adds, grouped by area with cross-links to their owning feature docs; the Fernet-encrypted credential pattern (limesurvey_pwd, google_ws_sa_json) and a real NameError bug fixed in its inverse method |
| res.config.settings (EMS extension) | The related-field proxy that makes every res.company field above editable from Settings; set_values()’s EMS auto-checkout cron activation |
| Model | Description |
|---|---|
| ems.space_type | Kinds of physical space (classroom, lab…); removed a stale TODO comment about a config page that already existed |
| ems.space | Physical spaces, widely referenced by scheduling/attendance/documentation models |
| Model | Description |
|---|---|
| ems.group | The core class-group model — one of the most widely-referenced in EMS; group_type switching, the tutor-role sync bug fix, the side-effecting enrollment_view_ids compute |
| Group schedule (read-only aggregation) | The group form’s “Schedule” tab: aggregating teachers’ resource.calendar.attendance rows by group_ids, deriving the break period from the level’s schedule framework, the “Subject → Teacher(s)” co-teaching summary, and the PDF export |
| Student schedule (read-only aggregation) | The student form’s own “Schedule” tab: the same read-only mechanism as the group’s, reused via the shared readonly_schedule_grid widget, but scoped per (subject_id, group_id) enrollment pair instead of a whole group — and the overlap-aware column-split layout that scoping makes necessary |
| ems.contact.data.request | Contact data update requests: sending them from the backend (individually or by group, study or level), the portal page students and families answer on, family-contact recognition and linking, and the review/approval flow that applies the staged changes |
| Topic | Description |
|---|---|
| hr.employee (EMS extension) | Map of the whole model — cross-links every already-documented area (roles, schedule, photo, Google Workspace) plus a real compute_sudo bug fix on read_only, new get_report_role_lines() branch coverage, and a flagged ems.group.create() gap |
| Academic role hierarchy | Teacher → Tutor → Department Chief → Head of Studies → Director → Administrator group chain and how roles sync to res.users.groups_id |
| Department Chief / Seminar Chief / Head of Studies / Director cascade | hr.department.manager_id/seminar_chief_id/is_top_level/top_level_role (Head of Studies/Deputy/Secretary) plus res.company.director_id driving hr.employee.parent_id (between departments and up to the Director) and the role_dchieff/role_seminar/role_hos/role_dhos/role_secretary/role_director roles automatically |
| hr.job (EMS extension) | Two fields only (employee_type, group_id) — the security-group auto-grant is consumed and already tested from hr.employee’s side |
| ems.workgroup | Simple free-form employee grouping (project teams, committees) — no business logic |
| ems.teaching | Ternary teacher/group/subject relation, derived from and kept in sync with the schedule via _sync_from_schedule() |
| ems.non_teaching_type | Catalogue of non-subject schedule period types (breaks, guard duties); note the admin group is group_department_chief, not the usual group_academic_admin |
| hr.attendance auto-checkout (EMS extension) | Closing stale open attendances on check-in, and the EMS nightly cron mode using the employee’s real schedule instead of fixed hours |
| res.users (EMS extension) | _sync_ems_implied_groups() — compensates for Odoo’s own implied-group grants being permanent/never auto-revoked |
| ems.tracking | Free-form student follow-up notes; flags a possible access-scope mismatch (teachers described as able to “add” notes but only granted read access) |
| Teacher working schedules & schedule frameworks | The “Schedule” tab widget, schedule frameworks, the empty-slot rule, employee lifecycle hooks, the XML import wizard |
| Google Workspace staff integration & EMS user auto-creation | Corporate Google account creation (Directory API), automatic res.users with OAuth pre-link, lifecycle sync (archive ↔ suspend), required-fields chain |
| Profile picture disable switch | The res.users.image_disabled toggle, keeping hr.employee/res.users photos in sync, and the write_photo() mimetype-safety helper |
| Model | Description |
|---|---|
| ems.attendance_template | Who teaches what, where and for whom: schedule sync/reconciliation from the “Schedule” tab and the XML importer, co-teaching, external-conflict detection |
| ems.attendance_status | Archivable model replacing the old hardcoded status enum; status_id field rename across the session line, justification, issue-notification and reporting code; the “Issue” status retirement now that ems.strike covers it |
| Guard Duty Schedule Board | Read-only, centre-wide view of every teacher’s occupied time blocks per weekday, with guard-duty teachers highlighted separately; extends ems.course, ems.non_teaching_type.is_guard |
| Model | Description |
|---|---|
| ems.strike | Disciplinary notices issued from the roll-call view: recipient/authorization rules, HoS/DHoS-branch escalation matching, access control |
| Model | Description |
|---|---|
| ems.meeting.presence | Meeting attendance confirmed with the NFC tag at a public token-protected kiosk: convened list by scope, scan outcomes, closing, access control, and how it joins the quality work’s minutes |
| Model | Description |
|---|---|
| ems.enrollment_proposal_wizard | Bulk draft enrollments from a student selection, and the secretary-only allow_other_study flag that enrolls a current student into a different study |
| Enrollment benefits | Fee bonifications/exemptions: draft-order recompute, freeze after confirmation and the secretary re-apply action that regenerates the invoice |
| Model | Description |
|---|---|
| ems.course_transition_wizard | End-of-year transition: study-scoped preview and apply, graduates archived as alumni, bulk placement from the destination enrollments and conditional course flip |
| Topic | Description |
|---|---|
| Free-pick color widget | widget="color" + the ems_color_swatch styling, the role_color_tags badge widget, and ems.hex_color_mixin — used by ems.role, ems.attendance_template, and hr.department’s custom_color |
| Task assignment | mail.activity.type’s ems_task_assignment/ems_assignee_ids — an explicit, config-driven recipient list decoupled from security groups |
ems.base |
Chatter/notification helpers (notify, chatter, chatter_exception), permission checks (get_user_is_admin/_tutor), persistent_hash — the foundational mixin inherited by most business models |
ems.datetime_utils |
Timezone-aware ↔ naive-UTC ↔ float-hour conversions shared by every attendance/schedule model |
| Dates, times and timezones | The one-timezone policy (always the company’s): server-side helpers, the web client’s company-timezone service and serverNow(), and the record-level tz alignment |
ems.multithreading |
The run_in_thread() setup/compute/store/callback engine behind the LimeSurvey integration’s long-running actions |
ems.schedule_report_mixin |
The shared weekly-schedule aggregation-to-report pipeline (report-line building, break derivation, colour/time-label helpers) behind both the group’s and the student’s own read-only Schedule tab |
google.workspace.mixin |
The Directory API client, password policy, and text/phone normalization shared by the staff and student Google Workspace integrations |
| User menu “Documentation” entry | Points the avatar dropdown’s native “Documentation” entry at EMS’s own user manuals, in the user’s language, instead of Odoo’s developer docs |
| Shared test utilities | tests/common.py: create_level_study(_group), mock_outgoing_email, make_synchronous_run_in_thread — fixture/mock boilerplate extracted after it was found duplicated across dozens of test files |
| Topic | Description |
|---|---|
| AI agent test notifications when running inside a container | Why push/desktop notifications from an AI coding agent don’t reach you inside an LXC/Incus/Docker container reached via an editor extension, and how to bridge them with a host-side file-drop watcher + a Claude Code hook |
More information about the project on the GitHub repository.