# EnCoach Platform — Odoo 19 Backend SRS

**Version:** 3.0  
**Date:** March 11, 2026  
**Status:** Implemented — Staging Verified  
**Supersedes:** `ODOO_BACKEND_SRS_v3.md`, `ODOO_MIGRATION_SRS_v2.md`, `ODOO_MIGRATION_SRS.md`  
**Frontend Reference:** `ENCOACH_UNIFIED_SRS.md` (v2.0)  
**Purpose:** Complete backend specification for the EnCoach Adaptive Learning Platform on Odoo 19. All modules, models, and REST API endpoints have been implemented and deployed to staging.

### Implementation Status

| Artifact | Location |
|----------|----------|
| **Backend Repository** | `https://git.albousalh.com/devops/encoach_backend_new_v2.git` (branch: `main`) |
| **Frontend Repository** | `https://git.albousalh.com/devops/encoach_frontend_new_v2.git` (branch: `main`) |
| **Staging Backend (Odoo 19)** | `http://5.189.151.117:8069` |
| **Staging Frontend** | `http://5.189.151.117:3000` |
| **Custom EnCoach Modules** | 27 modules in `new_project/custom_addons/` |
| **OpenEduCat Modules** | 14 community modules in `new_project/openeducat_erp-19.0/` |
| **Total API Routes** | ~377 REST endpoints across 4 controller packages |
| **Deployment** | Docker Compose (Odoo 19 + PostgreSQL 16) |

**Note:** The developer implemented all features in this SRS plus additional OpenEduCat Enterprise features documented in Section 34 (Beyond-SRS Features).

---

## Table of Contents

1. [Introduction](#1-introduction)
2. [Architecture Overview](#2-architecture-overview)
3. [OpenEduCat Integration Strategy](#3-openeducat-integration-strategy)
4. [Module Inventory](#4-module-inventory)
5. [Authentication and User Management](#5-authentication-and-user-management)
6. [Entity and Permission System](#6-entity-and-permission-system)
7. [LMS Core (OpenEduCat Bridge)](#7-lms-core-openeducat-bridge)
8. [Academic Year, Term, and Department](#8-academic-year-term-and-department)
9. [Admission and Enrollment](#9-admission-and-enrollment)
10. [Subject Registration](#10-subject-registration)
11. [Exam Engine (EnCoach AI)](#11-exam-engine-encoach-ai)
12. [Institutional Exams (OpenEduCat)](#12-institutional-exams-openeducat)
13. [Assignment System](#13-assignment-system)
14. [Course Assignment Submissions (OpenEduCat)](#14-course-assignment-submissions-openeducat)
15. [Classroom and Group Management](#15-classroom-and-group-management)
16. [Subject Taxonomy Engine](#16-subject-taxonomy-engine)
17. [Adaptive Learning Engine](#17-adaptive-learning-engine)
18. [Learning Resources](#18-learning-resources)
19. [AI Services](#19-ai-services)
20. [Training Content](#20-training-content)
21. [Analytics and Statistics](#21-analytics-and-statistics)
22. [Subscription and Payments](#22-subscription-and-payments)
23. [Support Tickets](#23-support-tickets)
24. [File Storage](#24-file-storage)
25. [Approval Workflows (Enhanced)](#25-approval-workflows-enhanced)
26. [Courseware and Content Delivery](#26-courseware-and-content-delivery)
27. [Communication System](#27-communication-system)
28. [Notification Engine](#28-notification-engine)
29. [FAQ System](#29-faq-system)
30. [Plagiarism Detection](#30-plagiarism-detection)
31. [Official Exam Access](#31-official-exam-access)
32. [Non-Functional Requirements](#32-non-functional-requirements)
33. [Implementation Priority](#33-implementation-priority)
34. [Beyond-SRS Features (Implemented)](#34-beyond-srs-features-implemented)

---

## 1. Introduction

### 1.1 Scope

This document specifies the complete Odoo 19 backend for the EnCoach Adaptive Learning Platform. The backend serves a React SPA frontend via REST API. All endpoints use JSON request/response with JWT authentication (except explicitly public endpoints).

### 1.2 Frontend Contract

The React frontend has 29 API service modules that define the exact endpoints the backend must implement. This SRS documents every endpoint, its expected request/response shape, and the underlying Odoo models.

### 1.3 Key Principles

- **OpenEduCat First:** All traditional LMS features (courses, batches, students, faculty, timetable, attendance, exams, assignments, admissions) use OpenEduCat community modules ported to Odoo 19.
- **EnCoach Extensions:** Custom modules extend OpenEduCat models with fields the platform needs (e.g., `description`, `image`, `max_capacity` on `op.course`).
- **REST API Layer:** All data is exposed via REST controllers in `encoach_api` and `encoach_lms_api` modules. The frontend never uses Odoo's web client or XML-RPC.
- **AI Services:** AI functionality (GPT, Whisper, Polly, ELAI, GPTZero, FAISS) is encapsulated in dedicated `encoach_ai_*` modules with API endpoints.

---

## 2. Architecture Overview

```
React SPA (Vite + TypeScript)
     │
     ▼ REST API (JSON + JWT)
┌─────────────────────────────────┐
│         Odoo 19 Server          │
│                                 │
│  ┌──────────┐  ┌──────────────┐ │
│  │ encoach_ │  │  OpenEduCat  │ │
│  │ modules  │  │  (ported v19)│ │
│  └──────────┘  └──────────────┘ │
│         │              │        │
│         ▼              ▼        │
│      PostgreSQL 16              │
└─────────────────────────────────┘
     │
     ▼ External Services
  OpenAI API │ AWS Polly │ ELAI │ GPTZero
```

### 2.1 Technology Stack

| Layer | Technology |
|-------|-----------|
| Framework | Odoo 19 Community Edition |
| Language | Python 3.12+ |
| Database | PostgreSQL 16 |
| LMS Foundation | OpenEduCat (ported from v17 to v19) |
| AI/ML | OpenAI GPT-4o, Whisper (local), AWS Polly, Sentence Transformers, FAISS |
| Authentication | JWT (PyJWT) |
| File Storage | Odoo ir.attachment + optional S3 |
| Deployment | Docker, Docker Compose |

---

## 3. OpenEduCat Integration Strategy

### 3.1 Modules to Port (v17 → v19)

| Module | Models | Porting Notes |
|--------|--------|---------------|
| `openeducat_core` | `op.course`, `op.batch`, `op.student`, `op.faculty`, `op.subject`, `op.department`, `op.category`, `op.academic.year`, `op.academic.term`, `op.student.course`, `op.subject.registration` | Core dependency for all other modules |
| `openeducat_timetable` | `op.session`, `op.timing` | Depends on `openeducat_core` |
| `openeducat_attendance` | `op.attendance.register`, `op.attendance.sheet`, `op.attendance.line`, `op.attendance.type` | Depends on `openeducat_core`, `openeducat_timetable` |
| `openeducat_classroom` | `op.classroom` | Depends on `openeducat_facility` |
| `openeducat_facility` | `op.facility`, `op.facility.line` | Dependency of classroom |
| `openeducat_exam` | `op.exam.session`, `op.exam`, `op.exam.type`, `op.exam.attendees`, `op.exam.room`, `op.marksheet.register`, `op.marksheet.line`, `op.result.line`, `op.result.template`, `op.grade.configuration` | Depends on `openeducat_core` |
| `openeducat_assignment` | `op.assignment`, `op.assignment.sub.line`, `grading.assignment`, `grading.assignment.type` | Depends on `openeducat_core` |
| `openeducat_admission` | `op.admission`, `op.admission.register` | Depends on `openeducat_core` |

### 3.2 Model Extensions (via `encoach_lms_api`)

The `encoach_lms_api` module uses `_inherit` to add fields missing from OpenEduCat but required by the frontend:

| Model | Added Fields |
|-------|-------------|
| `op.course` | `description` (Text), `image` (Binary), `max_capacity` (Integer), `status` (Selection: draft/active/archived), `department_id` (Many2one to `op.department`) |
| `op.batch` | `max_students` (Integer), `status` (Selection: draft/active/completed) |
| `op.student` | `enrollment_date` (Date), `status` (Selection: active/inactive/graduated/suspended) |
| `op.faculty` | `department_id` (Many2one to `op.department`), `specialization` (Char) |
| `op.subject` | `credits` (Float), `department_id` (Many2one to `op.department`) |

### 3.3 Serialization

All OpenEduCat models need `to_api_dict()` methods on the `encoach_lms_api` inheriting models that convert records to JSON-serializable dictionaries with snake_case keys. Many2one fields are serialized as `{field}_id` (integer) and `{field}_name` (string).

---

## 4. Module Inventory

| # | Module | Type | Description |
|---|--------|------|-------------|
| 1 | `openeducat_core` | OpenEduCat (port v19) | Course, batch, student, faculty, subject, department, academic year/term |
| 2 | `openeducat_timetable` | OpenEduCat (port v19) | Timetable sessions and timings |
| 3 | `openeducat_attendance` | OpenEduCat (port v19) | Attendance sheets, lines, registers |
| 4 | `openeducat_classroom` | OpenEduCat (port v19) | Physical classrooms |
| 5 | `openeducat_facility` | OpenEduCat (port v19) | Facility management |
| 6 | `openeducat_exam` | OpenEduCat (port v19) | Institutional exam sessions, marksheets, grading |
| 7 | `openeducat_assignment` | OpenEduCat (port v19) | Course assignments and submissions |
| 8 | `openeducat_admission` | OpenEduCat (port v19) | Admission registers and applications |
| 9 | `encoach_core` | Custom | `encoach.user` extensions on `res.users`, entities, roles, permissions, codes, invites |
| 10 | `encoach_api` | Custom | REST API controllers for auth, users, entities, permissions, exams, assignments, classrooms, tickets, subscriptions, stats, storage, approvals |
| 11 | `encoach_lms_api` | Custom | REST API bridge for OpenEduCat models (courses, batches, timetable, attendance, grades, academic years, departments, admissions, subject registration, institutional exams, marksheets, course assignments) |
| 12 | `encoach_ai` | Custom | OpenAI service wrapper, token counting, blacklist, system parameter management |
| 13 | `encoach_ai_media` | Custom | TTS (AWS Polly), STT (Whisper), ELAI avatar video generation |
| 14 | `encoach_ai_generation` | Custom | AI exam content generation for all subjects |
| 15 | `encoach_ai_grading` | Custom | AI grading for writing, speaking, math, IT |
| 16 | `encoach_exam` | Custom | `encoach.exam` model for AI-powered exams |
| 17 | `encoach_assignment` | Custom | `encoach.assignment` model (exam-wrapper assignments) |
| 18 | `encoach_evaluation` | Custom | Evaluation job tracking |
| 19 | `encoach_stats` | Custom | Session tracking and statistics |
| 20 | `encoach_training` | Custom | Training tips, walkthroughs, FAISS embeddings |
| 21 | `encoach_classroom` | Custom | `encoach.classroom.group` for virtual classrooms |
| 22 | `encoach_subscription` | Custom | Packages, payments, discounts, Stripe/PayPal/Paymob |
| 23 | `encoach_registration` | Custom | User registration and batch import |
| 24 | `encoach_ticket` | Custom | Support tickets |
| 25 | `encoach_taxonomy` | Custom | Subject, domain, topic, learning objective models |
| 26 | `encoach_resources` | Custom | Learning resource library |
| 27 | `encoach_adaptive` | Custom | Proficiency tracking, learning plans, progress, content cache |
| 28 | `encoach_adaptive_api` | Custom | REST controllers for adaptive learning |
| 29 | `encoach_adaptive_ai` | Custom | AI services for diagnostics, plan generation, coaching, content generation |
| 30 | `encoach_sis` | Custom | UTAS SIS integration (sync, mapping) |
| 31 | `encoach_branding` | Custom | Whitelabeling configuration |
| 32 | `encoach_courseware` | Custom | Course chapters, chapter materials, chapter progress tracking, AI workbench for content generation |
| 33 | `encoach_communication` | Custom | Discussion boards, threaded posts, announcements, direct messaging |
| 34 | `encoach_notification` | Custom | Notification engine, configurable rules, email/in-app delivery, user preferences |
| 35 | `encoach_faq` | Custom | FAQ categories and items, role-filtered, searchable |

**Original plan: 8 OpenEduCat + 27 custom = 35 modules**

### 4.2 Additional Modules (Implemented Beyond SRS)

The developer added 6 more OpenEduCat community modules:

| # | Module | Type | Description |
|---|--------|------|-------------|
| 36 | `openeducat_fees` | OpenEduCat (port v19) | Fee plans, student fees, fee terms |
| 37 | `openeducat_library` | OpenEduCat (port v19) | Media catalog, movements, library cards |
| 38 | `openeducat_activity` | OpenEduCat (port v19) | Student activities, activity types |
| 39 | `openeducat_parent` | OpenEduCat (port v19) | Parent-student relationships |
| 40 | `openeducat_erp` | OpenEduCat (port v19) | ERP connector meta-module |
| 41 | `theme_web_openeducat` | OpenEduCat (port v19) | Web theme |

**Deployed total: 14 OpenEduCat + 27 custom = 41 modules**

---

## 5. Authentication and User Management

### 5.1 Authentication Model

JWT-based authentication. Tokens are issued on login and validated on every API request.

### 5.2 User Model

Extends `res.users` with `encoach.user` mixin:

| Field | Type | Description |
|-------|------|-------------|
| `user_type` | Selection | `student`, `teacher`, `admin`, `corporate`, `mastercorporate`, `agent`, `developer` |
| `is_verified` | Boolean | Email verification status |
| `entity_ids` | Many2many | Associated entities |
| `phone` | Char | Phone number |
| `birth_date` | Date | Date of birth |
| `gender` | Selection | `m`, `f`, `o` |
| `op_student_id` | Many2one | Link to `op.student` if user_type is student |
| `op_faculty_id` | Many2one | Link to `op.faculty` if user_type is teacher |

### 5.3 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/login` | Public | Authenticate user, return JWT + user object |
| `POST` | `/api/logout` | JWT | Invalidate token |
| `GET` | `/api/user` | JWT | Get current authenticated user |
| `POST` | `/api/reset/sendVerification` | Public | Send password reset / verification email |
| `GET` | `/api/users/list` | JWT | Paginated user list with filters (`type`, `entity_id`, `page`, `size`) |
| `GET` | `/api/users/{id}` | JWT | Get user by ID |
| `PATCH` | `/api/users/update` | JWT | Update user (id in body) |
| `POST` | `/api/users/create` | JWT | Create user |
| `POST` | `/api/batch_users` | JWT | Batch create users from CSV/JSON |
| `GET` | `/api/users/export` | JWT | Export users as CSV blob |

### 5.4 Login Response Shape

```json
{
  "token": "eyJ...",
  "user": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "user_type": "admin",
    "is_verified": true,
    "entities": [
      { "id": 1, "name": "UTAS", "logo": "..." }
    ]
  }
}
```

---

## 6. Entity and Permission System

### 6.1 Entity Model (`encoach.entity`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Entity name |
| `code` | Char | Unique code |
| `type` | Selection | `corporate`, `university`, `freelance` |
| `logo` | Binary | Entity logo |
| `active` | Boolean | Active status |
| `user_ids` | Many2many | Members |
| `role_ids` | One2many | Custom roles |

### 6.2 Role and Permission Models

**`encoach.role`**

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Role name |
| `entity_id` | Many2one | Parent entity |
| `permission_ids` | Many2many | Assigned permissions |

**`encoach.permission`**

77+ entity-scoped permissions (e.g., `view_students`, `edit_exam`, `view_payment_record`, `manage_entities`).

### 6.3 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/entities` | JWT | Paginated entities |
| `GET` | `/api/entities/{id}` | JWT | Entity detail |
| `POST` | `/api/entities` | JWT | Create entity |
| `PATCH` | `/api/entities/{id}` | JWT | Update entity |
| `DELETE` | `/api/entities/{id}` | JWT | Delete entity |
| `GET` | `/api/entities/{entityId}/roles` | JWT | Roles for entity |
| `POST` | `/api/entities/{entityId}/roles` | JWT | Create role |
| `PATCH` | `/api/roles/{roleId}/permissions` | JWT | Update role permissions |
| `GET` | `/api/permissions` | JWT | List permissions (filter by `entity_id`) |

---

## 7. LMS Core (OpenEduCat Bridge)

### 7.1 Course API

Backed by `op.course` (extended via `_inherit` in `encoach_lms_api`).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/courses` | JWT | Paginated courses (filter: `status`, `department_id`) |
| `GET` | `/api/courses/{id}` | JWT | Course detail with subjects, batches |
| `POST` | `/api/courses` | JWT | Create course |
| `PATCH` | `/api/courses/{id}` | JWT | Update course |
| `DELETE` | `/api/courses/{id}` | JWT | Delete course |
| `POST` | `/api/courses/ai-generate` | JWT | AI-generate course outline (calls OpenAI) |

**Course Response Shape:**

```json
{
  "id": 1,
  "name": "IELTS Preparation",
  "code": "IELTS-101",
  "description": "Comprehensive IELTS preparation course",
  "department_id": 1,
  "department_name": "English",
  "max_capacity": 30,
  "status": "active",
  "subject_ids": [1, 2, 3],
  "subject_names": ["Reading", "Writing", "Listening"],
  "batch_count": 2,
  "student_count": 45,
  "image": "base64..."
}
```

### 7.2 Batch API

Backed by `op.batch` (extended).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/batches` | JWT | Paginated batches (filter: `course_id`, `status`) |
| `GET` | `/api/batches/{id}` | JWT | Batch detail with student list |
| `POST` | `/api/batches` | JWT | Create batch |
| `PATCH` | `/api/batches/{id}` | JWT | Update batch |
| `DELETE` | `/api/batches/{id}` | JWT | Delete batch |

### 7.3 Timetable API

Backed by `op.session` and `op.timing`.

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/timetable` | JWT | Sessions (filter: `course_id`, `batch_id`, `faculty_id`, `start_date`, `end_date`) |
| `POST` | `/api/timetable` | JWT | Create timetable session |

**Session Response Shape:**

```json
{
  "id": 1,
  "name": "IELTS Reading - Week 1",
  "course_id": 1,
  "course_name": "IELTS Preparation",
  "batch_id": 1,
  "batch_name": "Batch A",
  "faculty_id": 5,
  "faculty_name": "Dr. Smith",
  "subject_id": 1,
  "subject_name": "Reading",
  "classroom_id": 3,
  "classroom_name": "Room 101",
  "start_datetime": "2026-03-15T09:00:00",
  "end_datetime": "2026-03-15T10:30:00",
  "day": "Monday"
}
```

### 7.4 Attendance API

Backed by `op.attendance.sheet` and `op.attendance.line`.

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/attendance` | JWT | Attendance records (filter: `course_id`, `batch_id`, `student_id`, `date_from`, `date_to`) |
| `POST` | `/api/attendance` | JWT | Record attendance (batch of student attendance lines) |

**Attendance Record Request:**

```json
{
  "session_id": 1,
  "attendance_date": "2026-03-15",
  "lines": [
    { "student_id": 1, "present": true },
    { "student_id": 2, "present": false, "remark": "Sick leave" }
  ]
}
```

### 7.5 Grades API

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/grades` | JWT | Grade records (filter: `student_id`, `course_id`, `batch_id`) |

---

## 8. Academic Year, Term, and Department

### 8.1 Academic Year and Term

Backed by `op.academic.year` and `op.academic.term` (OpenEduCat core).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/academic-years` | JWT | List academic years with terms |
| `GET` | `/api/academic-years/{id}` | JWT | Year detail with terms |
| `POST` | `/api/academic-years` | JWT | Create academic year |
| `PATCH` | `/api/academic-years/{id}` | JWT | Update year |
| `DELETE` | `/api/academic-years/{id}` | JWT | Delete year |
| `POST` | `/api/academic-years/{id}/generate-terms` | JWT | Auto-generate terms from `term_structure` |
| `GET` | `/api/academic-terms` | JWT | List terms (filter: `year_id`) |
| `POST` | `/api/academic-terms` | JWT | Create term |
| `PATCH` | `/api/academic-terms/{id}` | JWT | Update term |
| `DELETE` | `/api/academic-terms/{id}` | JWT | Delete term |

**Generate Terms Logic:**

Given `term_structure` on the academic year, compute term dates:
- `two_sem`: 2 terms splitting the year in half
- `two_sem_qua`: 2 semesters, each with 2 quarters (4 terms total)
- `three_sem`: 3 trimesters
- `four_Quarter`: 4 quarters
- `final_year`: single term spanning the full year

### 8.2 Department

Backed by `op.department` (OpenEduCat core).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/departments` | JWT | List departments |
| `POST` | `/api/departments` | JWT | Create department |
| `PATCH` | `/api/departments/{id}` | JWT | Update department |
| `DELETE` | `/api/departments/{id}` | JWT | Delete department |

**Department Response:**

```json
{
  "id": 1,
  "name": "Department of Mathematics",
  "code": "MATH",
  "parent_id": null,
  "parent_name": null,
  "course_count": 5,
  "faculty_count": 12
}
```

---

## 9. Admission and Enrollment

### 9.1 Admission Register

Backed by `op.admission.register` (OpenEduCat).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/admission-registers` | JWT | List registers |
| `POST` | `/api/admission-registers` | JWT | Create register |
| `PATCH` | `/api/admission-registers/{id}` | JWT | Update register |
| `POST` | `/api/admission-registers/{id}/confirm` | JWT | Confirm register (opens enrollment) |
| `POST` | `/api/admission-registers/{id}/close` | JWT | Close register |

### 9.2 Admission

Backed by `op.admission` (OpenEduCat).

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/admissions` | JWT | List admissions (filter: `state`, `register_id`, `course_id`, `page`, `size`) |
| `GET` | `/api/admissions/{id}` | JWT | Admission detail |
| `POST` | `/api/admissions` | Public* | Submit admission application |
| `POST` | `/api/admissions/{id}/submit` | JWT | Submit for review |
| `POST` | `/api/admissions/{id}/confirm` | JWT | Confirm admission |
| `POST` | `/api/admissions/{id}/admit` | JWT | Admit applicant |
| `POST` | `/api/admissions/{id}/reject` | JWT | Reject admission |

*Note: `POST /api/admissions` is public when submitted via the `/apply` page. Backend should allow unauthenticated submission when the register allows public applications.

### 9.3 Admit Action Logic

When an admission is admitted (`/admit`):
1. Create `op.student` record from admission data
2. Create `res.users` record with `user_type=student`
3. Create `op.student.course` linking student to course/batch
4. Set `admission.student_id` to the new student
5. Set `admission.state` to `admission`
6. Send welcome email with login credentials

---

## 10. Subject Registration

### 10.1 Model

Backed by `op.subject.registration` (OpenEduCat core).

### 10.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/subject-registrations` | JWT | List registrations (filter: `student_id`, `course_id`, `state`) |
| `POST` | `/api/subject-registrations` | JWT | Create registration |
| `PATCH` | `/api/subject-registrations/{id}` | JWT | Update selected subjects |
| `POST` | `/api/subject-registrations/{id}/confirm` | JWT | Confirm registration |
| `POST` | `/api/subject-registrations/{id}/reject` | JWT | Reject registration |
| `GET` | `/api/subject-registrations/available` | JWT | Get available subjects for current student's enrolled courses |

### 10.3 Available Subjects Logic

For the current user (student):
1. Find all `op.student.course` records for this student
2. Get subjects linked to those courses
3. Exclude subjects already registered in confirmed registrations
4. Check unit load limits from `op.course` configuration
5. Return list with `min_unit_load`, `max_unit_load`

---

## 11. Exam Engine (EnCoach AI)

### 11.1 Model (`encoach.exam`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Exam title |
| `module` | Selection | `reading`, `writing`, `listening`, `speaking`, `math`, `it`, ... |
| `subject_id` | Many2one | Link to `encoach.subject` (taxonomy) |
| `topic_ids` | Many2many | Link to `encoach.topic` |
| `entity_id` | Many2one | Owning entity |
| `structure_id` | Many2one | Exam structure template |
| `rubric_id` | Many2one | Grading rubric |
| `exercises` | Text (JSON) | Generated exercises content |
| `is_public` | Boolean | Public access flag |
| `status` | Selection | `draft`, `published`, `archived` |

### 11.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/exam/{module}` | JWT | List exams for module (paginated) |
| `GET` | `/api/exam/{module}/{id}` | JWT | Exam detail |
| `POST` | `/api/exam` | JWT | Create exam |
| `PATCH` | `/api/exam/{id}` | JWT | Update exam |
| `DELETE` | `/api/exam/{id}` | JWT | Delete exam |
| `PATCH` | `/api/exam/{id}/access` | JWT | Set public/private |
| `GET` | `/api/rubrics` | JWT | List rubrics (paginated) |
| `POST` | `/api/rubrics` | JWT | Create rubric |
| `GET` | `/api/rubric-groups` | JWT | List rubric groups |
| `GET` | `/api/exam-structures` | JWT | List exam structures |
| `POST` | `/api/exam-structures` | JWT | Create exam structure |
| `DELETE` | `/api/exam-structures/{id}` | JWT | Delete structure |
| `GET` | `/api/exam/avatars` | JWT | List ELAI avatars |
| `POST` | `/api/exam/{module}/generate` | JWT | Generate exercises from existing exam |
| `POST` | `/api/exam/{module}/generate/scratch` | JWT | Generate full exam from scratch |

### 11.3 Generation Logic

Uses OpenAI GPT-4o to generate exam content. Prompt templates vary by module:
- **Reading/Listening:** Passage + comprehension questions
- **Writing:** Task description + sample topics
- **Speaking:** Dialogue prompts + follow-up questions
- **Math:** Problem sets with numerical answers, configurable difficulty
- **IT:** Code completion, debugging, conceptual questions

---

## 12. Institutional Exams (OpenEduCat)

### 12.1 Overview

Traditional institutional exams (midterms, finals) managed through OpenEduCat. Separate from EnCoach AI exams.

### 12.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/inst-exam-sessions` | JWT | List exam sessions |
| `POST` | `/api/inst-exam-sessions` | JWT | Create session |
| `GET` | `/api/inst-exam-sessions/{id}` | JWT | Session detail with exams |
| `PATCH` | `/api/inst-exam-sessions/{id}` | JWT | Update session |
| `DELETE` | `/api/inst-exam-sessions/{id}` | JWT | Delete session |
| `POST` | `/api/inst-exam-sessions/{id}/schedule` | JWT | Schedule session |
| `POST` | `/api/inst-exam-sessions/{id}/done` | JWT | Mark session done |
| `POST` | `/api/inst-exams` | JWT | Add exam to session |
| `PATCH` | `/api/inst-exams/{id}` | JWT | Update exam |
| `POST` | `/api/inst-exams/{id}/attendees` | JWT | Add attendees to exam |
| `PATCH` | `/api/inst-exams/{id}/marks` | JWT | Enter marks for attendees |
| `GET` | `/api/exam-types` | JWT | List exam types |
| `POST` | `/api/exam-types` | JWT | Create exam type |
| `GET` | `/api/grade-config` | JWT | List grade configurations |
| `POST` | `/api/grade-config` | JWT | Create grade config |
| `GET` | `/api/result-templates` | JWT | List result templates |
| `POST` | `/api/result-templates` | JWT | Create result template |
| `POST` | `/api/result-templates/{id}/generate` | JWT | Generate marksheets |
| `GET` | `/api/marksheets` | JWT | List marksheets |
| `GET` | `/api/marksheets/{id}` | JWT | Marksheet detail with student results |
| `POST` | `/api/marksheets/{id}/validate` | JWT | Validate marksheet |

### 12.3 Enter Marks Request

```json
{
  "attendees": [
    { "student_id": 1, "marks": 85 },
    { "student_id": 2, "marks": 72 }
  ]
}
```

### 12.4 Generate Marksheets Logic

1. Create `op.marksheet.register` from `op.result.template`
2. For each student in the batch:
   - Create `op.marksheet.line`
   - For each exam in the session: create `op.result.line` with marks from `op.exam.attendees`
   - Compute total marks, percentage
   - If evaluation_type is `grade`: lookup `op.grade.configuration` to assign grade
   - Compute pass/fail from `min_marks` on each exam
3. Compute `total_pass` and `total_failed` on register

---

## 13. Assignment System (EnCoach AI)

### 13.1 Model (`encoach.assignment`)

Exam-wrapper assignments where an EnCoach exam is assigned to students/classrooms.

### 13.2 Enhanced Fields (Late Submission + Plagiarism)

Add to `encoach.assignment`:

| Field | Type | Description |
|-------|------|-------------|
| `late_deadline` | Datetime | Final late submission cutoff |
| `penalty_type` | Selection | `none`, `percentage`, `fixed_deduction` |
| `penalty_value` | Float | Penalty amount |
| `max_submissions` | Integer | Maximum allowed submissions (0 = unlimited) |
| `allow_extensions` | Boolean | Student extension requests |
| `plagiarism_enabled` | Boolean | Enable GPTZero plagiarism check |
| `plagiarism_max_checks` | Integer | Max plagiarism checks per submission |
| `reminder_count` | Integer | Number of reminders |
| `reminder_frequency` | Selection | `once`, `daily`, `custom` |

### 13.3 Extension Request Model (`encoach.extension.request`)

| Field | Type | Description |
|-------|------|-------------|
| `student_id` | Many2one (`res.users`) | Requesting student |
| `assignment_id` | Many2one | Target assignment |
| `reason` | Text | Justification |
| `requested_date` | Datetime | New deadline |
| `status` | Selection | `pending`, `approved`, `rejected` |
| `approved_by` | Many2one | Approving teacher |
| `response_note` | Text | Teacher response |

### 13.4 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/assignments` | JWT | Paginated assignments |
| `GET` | `/api/assignments/{id}` | JWT | Assignment detail |
| `POST` | `/api/assignments` | JWT | Create assignment (includes late submission fields) |
| `PATCH` | `/api/assignments/{id}` | JWT | Update assignment |
| `DELETE` | `/api/assignments/{id}` | JWT | Delete assignment |
| `POST` | `/api/assignments/{id}/archive` | JWT | Archive assignment |
| `POST` | `/api/assignments/{id}/start` | JWT | Start assignment (student) |
| `POST` | `/api/assignments/{id}/request-extension` | JWT (student) | Request deadline extension |
| `GET` | `/api/assignments/{id}/extension-requests` | JWT (teacher) | List extension requests |
| `POST` | `/api/extension-requests/{id}/respond` | JWT (teacher) | Approve/reject extension |

---

## 14. Course Assignment Submissions (OpenEduCat)

### 14.1 Overview

Traditional file-based assignments where students upload work and teachers grade it. Uses OpenEduCat models.

### 14.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/course-assignments` | JWT | List course assignments (filter: `course_id`, `batch_id`, `state`) |
| `POST` | `/api/course-assignments` | JWT | Create course assignment |
| `GET` | `/api/course-assignments/{id}` | JWT | Assignment detail with submissions |
| `PATCH` | `/api/course-assignments/{id}` | JWT | Update assignment |
| `POST` | `/api/course-assignments/{id}/publish` | JWT | Publish assignment |
| `POST` | `/api/course-assignments/{id}/finish` | JWT | Close assignment |
| `GET` | `/api/course-assignments/{id}/submissions` | JWT | List student submissions |
| `POST` | `/api/course-assignments/{id}/submit` | JWT | Student file upload (multipart/form-data) |
| `PATCH` | `/api/submissions/{id}` | JWT | Grade submission (marks, feedback, status) |
| `GET` | `/api/assignment-types` | JWT | List assignment types |
| `POST` | `/api/assignment-types` | JWT | Create assignment type |

### 14.3 File Upload

Student submission accepts `multipart/form-data`:
- `file`: Binary file attachment
- `description`: Optional text description

Files are stored as `ir.attachment` records linked to `op.assignment.sub.line`.

### 14.4 Grading Request

```json
{
  "marks": 85,
  "state": "accept",
  "note": "Well researched paper. Minor formatting issues."
}
```

---

## 15. Classroom and Group Management

### 15.1 Model (`encoach.classroom.group`)

Virtual classrooms for grouping students within entities.

### 15.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/groups` | JWT | Paginated groups |
| `GET` | `/api/groups/{id}` | JWT | Group detail with members |
| `POST` | `/api/groups` | JWT | Create group |
| `DELETE` | `/api/groups/{id}` | JWT | Delete group |
| `POST` | `/api/groups/transfer` | JWT | Transfer students between groups |
| `POST` | `/api/groups/{id}/members` | JWT | Add members |
| `POST` | `/api/groups/{id}/members/remove` | JWT | Remove members |

---

## 16. Subject Taxonomy Engine

### 16.1 Models

**`encoach.subject`** (distinct from `op.subject`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Subject name (e.g., "IELTS", "Mathematics", "Information Technology") |
| `code` | Char | Unique code |
| `description` | Text | Description |
| `icon` | Char | Icon identifier |
| `active` | Boolean | Active flag |
| `domain_ids` | One2many | Child domains |

**`encoach.domain`**

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Domain name (e.g., "Algebra", "Networking") |
| `subject_id` | Many2one | Parent subject |
| `topic_ids` | One2many | Child topics |

**`encoach.topic`**

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Topic name |
| `domain_id` | Many2one | Parent domain |
| `subject_id` | Many2one (related) | Subject via domain |
| `difficulty` | Selection | `beginner`, `intermediate`, `advanced` |
| `prerequisite_ids` | Many2many | Prerequisite topics |
| `learning_objective_ids` | One2many | Learning objectives |

**`encoach.learning.objective`**

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Objective description |
| `topic_id` | Many2one | Parent topic |
| `bloom_level` | Selection | `remember`, `understand`, `apply`, `analyze`, `evaluate`, `create` |

### 16.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/subjects` | JWT | List subjects |
| `GET` | `/api/subjects/{id}` | JWT | Subject detail |
| `POST` | `/api/subjects` | JWT | Create subject |
| `PATCH` | `/api/subjects/{id}` | JWT | Update subject |
| `DELETE` | `/api/subjects/{id}` | JWT | Delete subject |
| `GET` | `/api/subjects/{subjectId}/taxonomy` | JWT | Full taxonomy tree (domains → topics → objectives) |
| `POST` | `/api/subjects/{subjectId}/taxonomy/import` | JWT | Import taxonomy from file (multipart) |
| `GET` | `/api/domains` | JWT | List domains (filter: `subject_id`) |
| `POST` | `/api/domains` | JWT | Create domain |
| `PATCH` | `/api/domains/{id}` | JWT | Update domain |
| `DELETE` | `/api/domains/{id}` | JWT | Delete domain |
| `POST` | `/api/domains/{domainId}/ai-suggest` | JWT | AI-suggest topics for domain |
| `GET` | `/api/topics` | JWT | List topics (filter: `domain_id`, `subject_id`) |
| `POST` | `/api/topics` | JWT | Create topic |
| `PATCH` | `/api/topics/{id}` | JWT | Update topic |
| `DELETE` | `/api/topics/{id}` | JWT | Delete topic |

---

## 17. Adaptive Learning Engine

### 17.1 Models

**`encoach.proficiency`**

| Field | Type | Description |
|-------|------|-------------|
| `student_id` | Many2one | Student user |
| `subject_id` | Many2one | Subject |
| `topic_id` | Many2one | Topic |
| `mastery_score` | Float | 0.0 to 1.0 |
| `confidence` | Float | Statistical confidence |
| `last_assessed` | Datetime | Last assessment time |
| `assessment_count` | Integer | Number of assessments |

**`encoach.learning.plan`**

| Field | Type | Description |
|-------|------|-------------|
| `student_id` | Many2one | Student |
| `subject_id` | Many2one | Subject |
| `status` | Selection | `active`, `paused`, `completed` |
| `items` | One2many | Plan items (topics in order) |
| `generated_by` | Selection | `ai`, `manual` |
| `progress` | Float | Computed completion percentage |

**`encoach.learning.plan.item`**

| Field | Type | Description |
|-------|------|-------------|
| `plan_id` | Many2one | Parent plan |
| `topic_id` | Many2one | Topic |
| `sequence` | Integer | Order in plan |
| `status` | Selection | `locked`, `available`, `in_progress`, `completed` |
| `estimated_hours` | Float | Estimated time |
| `mastery_threshold` | Float | Required mastery to complete |

**`encoach.diagnostic.session`**

| Field | Type | Description |
|-------|------|-------------|
| `student_id` | Many2one | Student |
| `subject_id` | Many2one | Subject |
| `status` | Selection | `in_progress`, `completed` |
| `questions_answered` | Integer | Count |
| `result_data` | Text (JSON) | Topic-level results |

**`encoach.content.cache`**

| Field | Type | Description |
|-------|------|-------------|
| `topic_id` | Many2one | Topic |
| `content_type` | Selection | `explanation`, `practice`, `quiz` |
| `content` | Text (JSON) | Cached AI content |
| `generated_at` | Datetime | Generation time |
| `ttl_hours` | Integer | Cache TTL |

### 17.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/diagnostic/start` | JWT | Start diagnostic for subject |
| `POST` | `/api/diagnostic/answer` | JWT | Submit diagnostic answer (adaptive next question) |
| `GET` | `/api/diagnostic/{sessionId}/result` | JWT | Get diagnostic result |
| `GET` | `/api/proficiency` | JWT | Get proficiency records (filter: `subject_id`, `student_id`) |
| `GET` | `/api/proficiency/summary` | JWT | Aggregate proficiency summary |
| `GET` | `/api/proficiency/class` | JWT | Class-wide proficiency |
| `GET` | `/api/learning-plan` | JWT | Get learning plan (filter: `subject_id`) |
| `POST` | `/api/learning-plan/generate` | JWT | AI-generate learning plan |
| `PATCH` | `/api/learning-plan/{id}` | JWT | Update plan |
| `POST` | `/api/learning-plan/{id}/pause` | JWT | Pause plan |
| `POST` | `/api/learning-plan/{id}/resume` | JWT | Resume plan |
| `GET` | `/api/topics/{topicId}/content` | JWT | Get topic learning content |
| `POST` | `/api/topics/{topicId}/generate-content` | JWT | AI-generate topic content |
| `POST` | `/api/topics/{topicId}/practice` | JWT | Generate practice questions |
| `POST` | `/api/topics/{topicId}/practice/grade` | JWT | Grade practice answers |
| `POST` | `/api/topics/{topicId}/mastery-quiz` | JWT | Start mastery quiz |
| `POST` | `/api/topics/{topicId}/mastery-quiz/submit` | JWT | Submit mastery quiz and update proficiency |

### 17.3 Diagnostic Algorithm

Computer Adaptive Testing (CAT):
1. Start with medium-difficulty questions
2. If correct → harder question; if incorrect → easier question
3. Uses Item Response Theory (IRT) to estimate ability
4. Covers multiple topics within the subject
5. Terminates after sufficient confidence or max questions (20-30)
6. Produces per-topic mastery scores

### 17.4 Learning Plan Generation

AI-generated study plan:
1. Read student's proficiency profile
2. Identify weak topics (mastery < threshold)
3. Respect prerequisite graph (topological sort)
4. Estimate time per topic based on gap
5. Order topics: prerequisites first, then weakest areas
6. Return ordered list of `plan_items`

---

## 18. Learning Resources

### 18.1 Model (`encoach.resource`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Resource title |
| `type` | Selection | `video`, `document`, `link`, `interactive` |
| `subject_id` | Many2one | Subject |
| `topic_ids` | Many2many | Topics |
| `file` | Binary | Uploaded file |
| `url` | Char | External URL |
| `difficulty` | Selection | `beginner`, `intermediate`, `advanced` |
| `duration_minutes` | Integer | Estimated duration |
| `active` | Boolean | Soft-delete flag |

### 18.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/resources` | JWT | Paginated resources (filter: `subject_id`, `topic_id`, `type`, `difficulty`) |
| `POST` | `/api/resources` | JWT | Upload resource (multipart) |
| `PATCH` | `/api/resources/{id}` | JWT | Update resource metadata |
| `DELETE` | `/api/resources/{id}` | JWT | Soft-delete resource |
| `POST` | `/api/resources/{id}/complete` | JWT | Mark resource as completed by student |
| `POST` | `/api/resources/{id}/rate` | JWT | Rate resource (1-5) |
| `GET` | `/api/resources/{id}/download` | JWT | Download resource file |

---

## 19. AI Services

### 19.1 AI Coaching

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/coach/chat` | JWT | General AI chat (context-aware) |
| `POST` | `/api/coach/hint` | JWT | Hint for topic/question |
| `POST` | `/api/coach/explain` | JWT | Explain scores or concepts |
| `POST` | `/api/coach/suggest` | JWT | Study suggestions |
| `POST` | `/api/coach/writing-help` | JWT | Writing feedback with improved text |
| `GET` | `/api/coach/tip` | JWT | Context-aware tip (FAISS similarity) |

### 19.2 AI Evaluation

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/evaluate/writing` | JWT | Evaluate writing (IELTS band scoring / rubric-based) |
| `POST` | `/api/evaluate/speaking` | JWT | Evaluate speaking (audio → transcribe → grade) |
| `POST` | `/api/grading/multiple` | JWT | Grade multiple-choice / numerical answers |
| `POST` | `/api/transcribe` | JWT | Audio transcription (Whisper, multipart upload) |

### 19.3 AI Generation

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/exam/{module}/generate` | JWT | Generate exam exercises from template |
| `POST` | `/api/exam/{module}/generate/scratch` | JWT | Generate full exam from scratch |

### 19.4 Media Generation

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/exam/listening/media` | JWT | Generate listening audio (AWS Polly TTS) |
| `POST` | `/api/exam/speaking/media` | JWT | Generate speaking video (ELAI) |
| `GET` | `/api/exam/avatars` | JWT | List ELAI avatars with voice metadata |

### 19.5 AI Analytics

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/ai/search` | JWT | AI-powered semantic search |
| `POST` | `/api/ai/insights` | JWT | AI-generated insights from data |
| `GET` | `/api/ai/alerts` | JWT | AI-detected alerts (at-risk students, anomalies) |
| `POST` | `/api/ai/report-narrative` | JWT | AI narrative for reports |
| `POST` | `/api/ai/batch-optimize` | JWT | Batch optimization suggestions |
| `POST` | `/api/ai/grade-suggest` | JWT | AI grading suggestion |

### 19.6 AI Service Configuration

All AI services read configuration from `ir.config_parameter`:

| Parameter Key | Description | Default |
|--------------|-------------|---------|
| `encoach.openai_api_key` | OpenAI API key | Required |
| `encoach.openai_model` | Default model | `gpt-4o` |
| `encoach.openai_model_light` | Light model for simple tasks | `gpt-3.5-turbo` |
| `encoach.aws_access_key_id` | AWS access key | Required |
| `encoach.aws_secret_access_key` | AWS secret key | Required |
| `encoach.aws_region` | AWS region | `eu-west-1` |
| `encoach.polly_voice_id` | Default Polly voice | `Joanna` |
| `encoach.polly_engine` | Polly engine | `neural` |
| `encoach.whisper_model` | Whisper model size | `base` |
| `encoach.whisper_workers` | Whisper worker threads | `4` |
| `encoach.elai_api_key` | ELAI API key | Required |
| `encoach.gptzero_api_key` | GPTZero API key | Required |
| `encoach.faiss_embedding_model` | Sentence transformer model | `all-MiniLM-L6-v2` |

---

## 20. Training Content

### 20.1 Models

**`encoach.training.tip`**

| Field | Type | Description |
|-------|------|-------------|
| `title` | Char | Tip title |
| `content` | Text | Tip content |
| `subject_id` | Many2one | Subject |
| `module` | Char | Module tag |
| `embedding` | Binary(attachment=False) | FAISS embedding vector |

**`encoach.training.walkthrough`**

| Field | Type | Description |
|-------|------|-------------|
| `title` | Char | Walkthrough title |
| `steps` | Text (JSON) | Step-by-step content |
| `module` | Char | Module tag |

### 20.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/training` | JWT | Training records (filter: `entity_id`, `user_id`, `module`) |
| `GET` | `/api/training/tips` | JWT | Training tips |
| `GET` | `/api/training/walkthrough` | JWT | Walkthroughs (filter: `module`) |

---

## 21. Analytics and Statistics

### 21.1 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/sessions` | JWT | Exam sessions |
| `GET` | `/api/stats` | JWT | Exam statistics |
| `GET` | `/api/statistical` | JWT | Aggregated statistical data |
| `GET` | `/api/stats/performance` | JWT | Performance metrics |
| `GET` | `/api/analytics/student` | JWT | Student analytics (filter: `student_id`, `subject_id`, `date_range`) |
| `GET` | `/api/analytics/class` | JWT | Class analytics (filter: `batch_id`, `course_id`) |
| `GET` | `/api/analytics/subject` | JWT | Subject analytics (filter: `subject_id`) |
| `GET` | `/api/analytics/content-gaps` | JWT | Content gap analysis |

---

## 22. Subscription and Payments

### 22.1 Models

**`encoach.package`**

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Package name |
| `description` | Text | Description |
| `price` | Float | Price |
| `currency` | Char | Currency code |
| `duration_days` | Integer | Access duration |
| `subject_ids` | Many2many | Included subjects |
| `features` | Text (JSON) | Feature list |
| `active` | Boolean | Available for purchase |

**`encoach.payment`**

| Field | Type | Description |
|-------|------|-------------|
| `user_id` | Many2one | Paying user |
| `entity_id` | Many2one | Entity (if institutional) |
| `package_id` | Many2one | Package |
| `amount` | Float | Payment amount |
| `currency` | Char | Currency |
| `provider` | Selection | `stripe`, `paypal`, `paymob`, `invoice` |
| `status` | Selection | `pending`, `completed`, `failed`, `refunded` |
| `provider_ref` | Char | External reference |
| `created_at` | Datetime | Payment date |

**`encoach.discount`**

| Field | Type | Description |
|-------|------|-------------|
| `code` | Char | Discount code |
| `percentage` | Float | Discount percentage |
| `valid_from` | Datetime | Start date |
| `valid_to` | Datetime | End date |
| `max_uses` | Integer | Maximum uses |
| `current_uses` | Integer | Current use count |

### 22.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/packages` | JWT | List packages |
| `GET` | `/api/payments` | JWT | Paginated payments |
| `POST` | `/api/stripe/checkout` | JWT | Create Stripe checkout session, return URL |
| `POST` | `/api/paypal/checkout` | JWT | Create PayPal order, return approval URL |
| `POST` | `/api/paymob/checkout` | JWT | Create Paymob payment, return URL |
| `GET` | `/api/discounts` | JWT | List discounts |
| `POST` | `/api/discounts` | JWT | Create discount |
| `DELETE` | `/api/discounts/{id}` | JWT | Delete discount |

### 22.3 Webhook Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/stripe/webhook` | Webhook signature | Stripe payment events |
| `POST` | `/api/paypal/webhook` | Webhook signature | PayPal payment events |
| `POST` | `/api/paymob/webhook` | Webhook HMAC | Paymob transaction callback |

---

## 23. Support Tickets

### 23.1 Model (`encoach.ticket`)

| Field | Type | Description |
|-------|------|-------------|
| `subject` | Char | Ticket subject |
| `description` | Text | Description |
| `type` | Selection | `bug`, `feature`, `support`, `feedback` |
| `status` | Selection | `open`, `in_progress`, `resolved`, `closed` |
| `reporter_id` | Many2one | Reporter user |
| `assignee_id` | Many2one | Assigned staff |
| `entity_id` | Many2one | Entity context |
| `source` | Selection | `platform`, `webpage` |

### 23.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/tickets` | JWT | Paginated tickets (filter: `status`, `type`, `assignee_id`, `source`) |
| `POST` | `/api/tickets` | JWT | Create ticket |
| `PATCH` | `/api/tickets/{id}` | JWT | Update ticket |
| `GET` | `/api/tickets/assignedToUser` | JWT | Tickets assigned to current user |

---

## 24. File Storage

### 24.1 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/storage/upload` | JWT | Upload file (multipart/form-data), return attachment ID and URL |
| `GET` | `/api/storage/url` | JWT | Resolve download URL for filename |

Files are stored as `ir.attachment` records. For production, configure Odoo to use S3-compatible storage.

---

## 25. Approval Workflows (Enhanced)

### 25.1 Model (`encoach.approval.workflow`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Workflow name |
| `type` | Selection | `exam_publish`, `assignment_publish`, `content_publish`, `material_publish` |
| `stage_ids` | One2many | Approval stages (ordered) |
| `entity_id` | Many2one | Entity |
| `allow_bypass` | Boolean | Whether bypass is allowed |
| `active` | Boolean | Active flag |

### 25.2 Model (`encoach.approval.stage`)

| Field | Type | Description |
|-------|------|-------------|
| `workflow_id` | Many2one | Parent workflow |
| `sequence` | Integer | Stage order |
| `approver_id` | Many2one | Assigned approver (`res.users`) |
| `max_days` | Integer | Maximum days before auto-escalation |
| `auto_escalate` | Boolean | Whether to auto-escalate on timeout |
| `notification_email` | Char | Email for stage notifications |
| `status` | Selection | `pending`, `approved`, `rejected`, `escalated`, `bypassed` |
| `comment` | Text | Reviewer comment |
| `acted_at` | Datetime | When action was taken |

### 25.3 Model (`encoach.approval.request`)

| Field | Type | Description |
|-------|------|-------------|
| `workflow_id` | Many2one | Workflow template |
| `res_model` | Char | Target model (e.g., `encoach.exam`) |
| `res_id` | Integer | Target record ID |
| `current_stage_id` | Many2one | Current active stage |
| `requester_id` | Many2one | User who submitted for approval |
| `state` | Selection | `in_progress`, `approved`, `rejected`, `bypassed` |
| `bypass_reason` | Text | Justification if bypassed |
| `created_at` | Datetime | Request creation time |

### 25.4 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/approval-workflows` | JWT | Paginated workflows |
| `POST` | `/api/approval-workflows` | JWT | Create workflow with stages |
| `GET` | `/api/approval-workflows/{id}` | JWT | Get workflow with stages |
| `PATCH` | `/api/approval-workflows/{id}` | JWT | Update workflow and stages |
| `DELETE` | `/api/approval-workflows/{id}` | JWT | Delete workflow |
| `POST` | `/api/approval-requests` | JWT | Submit item for approval |
| `GET` | `/api/approval-requests` | JWT | List approval requests (filter: state, type) |
| `POST` | `/api/approval-requests/{id}/approve` | JWT | Approve current stage |
| `POST` | `/api/approval-requests/{id}/reject` | JWT | Reject at current stage |
| `POST` | `/api/approval-requests/{id}/bypass` | JWT | Bypass approval (requires justification) |
| `POST` | `/api/approval-requests/{id}/return` | JWT | Return to specific stage |

### 25.5 Cron: Auto-Escalation

A server cron runs daily to check approval stages that have exceeded `max_days`. For stages with `auto_escalate = True`, the system automatically advances to the next stage and sends a notification email to the new approver with escalation reason.

---

## 26. Courseware and Content Delivery

**Module:** `encoach_courseware`  
**Dependencies:** `encoach_core`, `openeducat_core`, `encoach_ai`

### 26.1 Model (`encoach.course.chapter`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char (required) | Chapter title |
| `course_id` | Many2one (`op.course`) | Parent course |
| `sequence` | Integer | Display order |
| `description` | Text | Chapter description |
| `start_date` | Datetime | Scheduled start date |
| `end_date` | Datetime | Optional end date |
| `unlock_mode` | Selection | `auto_date`, `manual`, `prerequisite` |
| `is_unlocked` | Boolean | Current unlock status |
| `topic_id` | Many2one (`encoach.topic`) | Bridge to adaptive engine |
| `material_ids` | One2many | Chapter materials |
| `assignment_ids` | Many2many (`encoach.assignment`) | Linked assignments |
| `exam_ids` | Many2many (`encoach.exam`) | Linked exams/quizzes |
| `active` | Boolean | Soft-delete flag |

### 26.2 Model (`encoach.chapter.material`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char (required) | Material title |
| `chapter_id` | Many2one | Parent chapter |
| `type` | Selection | `pdf`, `document`, `video`, `audio`, `image`, `link`, `article` |
| `file` | Binary (attachment=True) | Uploaded file |
| `url` | Char | External URL |
| `description` | Text | Description |
| `sequence` | Integer | Display order |
| `allow_download` | Boolean | Student download permission |
| `is_book` | Boolean | Course book flag |
| `book_chapters` | Text | JSON book chapter structure |
| `active` | Boolean | Soft-delete flag |

### 26.3 Model (`encoach.chapter.progress`)

| Field | Type | Description |
|-------|------|-------------|
| `student_id` | Many2one (`res.users`) | Student |
| `chapter_id` | Many2one | Chapter |
| `status` | Selection | `not_started`, `in_progress`, `completed` |
| `started_at` | Datetime | First access time |
| `completed_at` | Datetime | Completion time |
| `materials_completed` | Integer | Count of materials viewed |
| `materials_total` | Integer | Computed total materials |

### 26.4 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/courses/{courseId}/chapters` | JWT | List chapters (ordered by sequence) |
| `POST` | `/api/courses/{courseId}/chapters` | JWT (teacher) | Create chapter |
| `GET` | `/api/chapters/{id}` | JWT | Chapter detail with material count |
| `PATCH` | `/api/chapters/{id}` | JWT (teacher) | Update chapter |
| `DELETE` | `/api/chapters/{id}` | JWT (teacher) | Soft-delete chapter |
| `POST` | `/api/chapters/{id}/unlock` | JWT (teacher) | Manually unlock |
| `POST` | `/api/chapters/{id}/lock` | JWT (teacher) | Manually lock |
| `PATCH` | `/api/courses/{courseId}/chapters/reorder` | JWT (teacher) | Reorder (body: `{ids: []}`) |
| `GET` | `/api/chapters/{id}/progress` | JWT | Student progress |
| `POST` | `/api/chapters/{id}/progress/complete` | JWT (student) | Mark chapter completed |
| `GET` | `/api/chapters/{chapterId}/materials` | JWT | List materials |
| `POST` | `/api/chapters/{chapterId}/materials` | JWT (teacher) | Upload material (multipart) |
| `PATCH` | `/api/materials/{id}` | JWT (teacher) | Update material metadata |
| `DELETE` | `/api/materials/{id}` | JWT (teacher) | Delete material |
| `GET` | `/api/materials/{id}/download` | JWT | Download file (streaming response) |
| `POST` | `/api/materials/{id}/viewed` | JWT (student) | Record material view |
| `PATCH` | `/api/chapters/{chapterId}/materials/reorder` | JWT (teacher) | Reorder materials |

### 26.5 AI Workbench Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/workbench/generate-outline` | JWT (teacher) | AI generates course outline from requirements (topic, objectives, complexity) |
| `POST` | `/api/workbench/generate-chapter` | JWT (teacher) | AI generates content for a specific chapter |
| `POST` | `/api/workbench/generate-rubric` | JWT (teacher) | AI generates grading rubric |
| `POST` | `/api/workbench/regenerate` | JWT (teacher) | Regenerate specific section |
| `POST` | `/api/workbench/suggest-materials` | JWT (teacher) | AI suggests supplementary materials based on student performance profiles |
| `POST` | `/api/workbench/publish` | JWT (teacher) | Publish generated content to course chapters (may trigger approval workflow) |

AI Workbench uses GPT-4o (temp 0.7 for generation) and calls `encoach_ai` service layer.

### 26.6 Cron: Auto-Unlock Chapters

Server cron runs every hour. For chapters with `unlock_mode = 'auto_date'` and `start_date <= now()` and `is_unlocked = False`, set `is_unlocked = True` and trigger notification.

---

## 27. Communication System

**Module:** `encoach_communication`  
**Dependencies:** `encoach_core`, `openeducat_core`

### 27.1 Model (`encoach.discussion.board`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char (required) | Board title |
| `course_id` | Many2one (`op.course`) | Associated course |
| `batch_id` | Many2one (`op.batch`) | Associated batch (optional) |
| `chapter_id` | Many2one (`encoach.course.chapter`) | Associated chapter (optional) |
| `assignment_id` | Many2one | Associated assignment (optional) |
| `is_enabled` | Boolean | Teacher toggle |
| `allow_student_posts` | Boolean | Student thread creation permission |
| `post_count` | Integer | Computed count of posts |

### 27.2 Model (`encoach.discussion.post`)

| Field | Type | Description |
|-------|------|-------------|
| `board_id` | Many2one | Parent board |
| `parent_id` | Many2one (self) | Parent post for threading |
| `author_id` | Many2one (`res.users`) | Author |
| `title` | Char | Thread title (root posts only) |
| `content` | Text | Post body (markdown) |
| `attachment_ids` | Many2many (`ir.attachment`) | File attachments |
| `is_pinned` | Boolean | Teacher pin |
| `is_resolved` | Boolean | Resolved flag (Q&A mode) |
| `reply_count` | Integer | Computed child count |
| `created_at` | Datetime | Post timestamp |

### 27.3 Model (`encoach.announcement`)

| Field | Type | Description |
|-------|------|-------------|
| `title` | Char (required) | Announcement title |
| `content` | Text | Body (markdown) |
| `author_id` | Many2one (`res.users`) | Author |
| `course_id` | Many2one (`op.course`) | Target course (null = system-wide) |
| `batch_id` | Many2one (`op.batch`) | Target batch (null = all batches) |
| `priority` | Selection | `normal`, `important`, `urgent` |
| `is_published` | Boolean | Published status |
| `published_at` | Datetime | Publish timestamp |
| `expires_at` | Datetime | Expiry time |
| `send_email` | Boolean | Also send via email |
| `attachment_ids` | Many2many (`ir.attachment`) | Attachments |

### 27.4 Model (`encoach.message`)

| Field | Type | Description |
|-------|------|-------------|
| `sender_id` | Many2one (`res.users`) | Sender |
| `recipient_id` | Many2one (`res.users`) | Recipient |
| `subject` | Char | Subject line |
| `content` | Text | Message body |
| `is_read` | Boolean | Read flag |
| `read_at` | Datetime | Read timestamp |
| `attachment_ids` | Many2many (`ir.attachment`) | Attachments |
| `send_email_copy` | Boolean | Send email copy |
| `created_at` | Datetime | Sent time |

### 27.5 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/discussion-boards` | JWT | List boards (filter: `course_id`, `batch_id`) |
| `POST` | `/api/discussion-boards` | JWT (teacher) | Create board |
| `PATCH` | `/api/discussion-boards/{id}` | JWT (teacher) | Update settings |
| `GET` | `/api/discussion-boards/{id}/posts` | JWT | Paginated posts (threaded) |
| `POST` | `/api/discussion-boards/{id}/posts` | JWT | Create post/reply |
| `PATCH` | `/api/posts/{id}` | JWT | Update own post |
| `DELETE` | `/api/posts/{id}` | JWT (teacher/admin) | Delete post |
| `POST` | `/api/posts/{id}/pin` | JWT (teacher) | Pin/unpin toggle |
| `POST` | `/api/posts/{id}/resolve` | JWT (teacher) | Mark as resolved |
| `GET` | `/api/announcements` | JWT | List announcements (filter: `course_id`, `priority`) |
| `POST` | `/api/announcements` | JWT (teacher/admin) | Create announcement |
| `PATCH` | `/api/announcements/{id}` | JWT | Update |
| `DELETE` | `/api/announcements/{id}` | JWT | Delete |
| `POST` | `/api/announcements/{id}/publish` | JWT | Publish |
| `GET` | `/api/messages` | JWT | Inbox (filter: `is_read`) |
| `GET` | `/api/messages/sent` | JWT | Sent messages |
| `POST` | `/api/messages` | JWT | Send message |
| `GET` | `/api/messages/{id}` | JWT | Message detail (auto-marks read) |
| `DELETE` | `/api/messages/{id}` | JWT | Delete message |
| `GET` | `/api/messages/unread-count` | JWT | Unread count `{count: N}` |

### 27.6 Email Integration

When `send_email = True` on announcements or `send_email_copy = True` on messages, the module uses Odoo's `mail.thread` mixin to send emails through the configured outgoing mail server.

---

## 28. Notification Engine

**Module:** `encoach_notification`  
**Dependencies:** `encoach_core`

### 28.1 Model (`encoach.notification`)

| Field | Type | Description |
|-------|------|-------------|
| `user_id` | Many2one (`res.users`) | Recipient |
| `title` | Char | Notification title |
| `message` | Text | Body |
| `type` | Selection | `deadline`, `chapter_unlock`, `result_release`, `announcement`, `assignment`, `exam`, `message`, `system` |
| `action_url` | Char | Frontend URL to navigate to |
| `is_read` | Boolean | Read status |
| `read_at` | Datetime | Read timestamp |
| `channel` | Selection | `in_app`, `email`, `both` |
| `created_at` | Datetime | Created time |

### 28.2 Model (`encoach.notification.rule`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Rule name |
| `event_type` | Selection | `assignment_due`, `exam_due`, `chapter_unlock`, `result_release`, `submission_graded`, `extension_response` |
| `days_before` | Integer | Days before event to trigger |
| `frequency` | Selection | `once`, `daily`, `custom` |
| `custom_intervals` | Text (JSON) | Custom intervals array (e.g., `[7, 3, 1]`) |
| `channel` | Selection | `in_app`, `email`, `both` |
| `entity_id` | Many2one | Entity scope |
| `active` | Boolean | Active flag |

### 28.3 Model (`encoach.notification.preferences`)

| Field | Type | Description |
|-------|------|-------------|
| `user_id` | Many2one (`res.users`) | One per user |
| `email_enabled` | Boolean | Master email toggle |
| `assignment_alerts` | Boolean | Assignment notifications |
| `exam_alerts` | Boolean | Exam notifications |
| `chapter_alerts` | Boolean | Chapter unlock notifications |
| `announcement_alerts` | Boolean | Announcement notifications |
| `message_alerts` | Boolean | Message notifications |

### 28.4 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/notifications` | JWT | Paginated list (filter: `type`, `is_read`) |
| `POST` | `/api/notifications/{id}/read` | JWT | Mark as read |
| `POST` | `/api/notifications/read-all` | JWT | Mark all as read |
| `GET` | `/api/notifications/unread-count` | JWT | `{count: N}` |
| `GET` | `/api/notification-rules` | JWT (admin) | List rules |
| `POST` | `/api/notification-rules` | JWT (admin) | Create rule |
| `PATCH` | `/api/notification-rules/{id}` | JWT (admin) | Update rule |
| `DELETE` | `/api/notification-rules/{id}` | JWT (admin) | Delete rule |
| `GET` | `/api/notification-preferences` | JWT | Get user preferences |
| `PATCH` | `/api/notification-preferences` | JWT | Update preferences |

### 28.5 Crons

1. **Deadline Reminders:** Runs daily. Checks all active rules, matches upcoming events (assignments, exams) within `days_before`, creates notifications and sends emails per user preferences.
2. **Chapter Unlock Notifications:** Triggered by the courseware cron when chapters are auto-unlocked.

---

## 29. FAQ System

**Module:** `encoach_faq`  
**Dependencies:** `encoach_core`

### 29.1 Model (`encoach.faq.category`)

| Field | Type | Description |
|-------|------|-------------|
| `name` | Char | Category name |
| `sequence` | Integer | Display order |
| `icon` | Char | Icon identifier |
| `audience` | Selection | `student`, `entity`, `both` |
| `active` | Boolean | Active flag |

### 29.2 Model (`encoach.faq.item`)

| Field | Type | Description |
|-------|------|-------------|
| `question` | Char | Question text |
| `answer` | Text | Answer (markdown, embedded images/video) |
| `category_id` | Many2one | Parent category |
| `audience` | Selection | `student`, `entity`, `both` |
| `image` | Binary | Optional image |
| `video_url` | Char | Optional video URL |
| `sequence` | Integer | Display order |
| `active` | Boolean | Active flag |

### 29.3 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/faq/categories` | Public/JWT | List categories (filter: `audience`) |
| `POST` | `/api/faq/categories` | JWT (admin) | Create category |
| `PATCH` | `/api/faq/categories/{id}` | JWT (admin) | Update category |
| `DELETE` | `/api/faq/categories/{id}` | JWT (admin) | Delete category |
| `GET` | `/api/faq/items` | Public/JWT | List items (filter: `category_id`, `audience`, `search`) |
| `POST` | `/api/faq/items` | JWT (admin) | Create item |
| `PATCH` | `/api/faq/items/{id}` | JWT (admin) | Update item |
| `DELETE` | `/api/faq/items/{id}` | JWT (admin) | Delete item |

---

## 30. Plagiarism Detection

**Module enhancement:** `encoach_ai_grading` (add plagiarism sub-service)

### 30.1 Plagiarism Report Storage

Add to `encoach.submission` or create `encoach.plagiarism.report`:

| Field | Type | Description |
|-------|------|-------------|
| `submission_id` | Many2one | Target submission |
| `overall_score` | Float | AI probability score (0-1) |
| `per_sentence` | Text (JSON) | Per-sentence analysis from GPTZero |
| `report_pdf` | Binary | Generated PDF report |
| `checked_at` | Datetime | When check was run |

### 30.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/plagiarism/check` | JWT (teacher) | Submit text for GPTZero analysis |
| `GET` | `/api/plagiarism/report/{submissionId}` | JWT | Get plagiarism report |
| `GET` | `/api/plagiarism/report/{submissionId}/download` | JWT | Download PDF report |

### 30.3 Integration

- Uses GPTZero `v2/predict/text` endpoint (already configured in `encoach_ai_grading`)
- Can be enabled/disabled per assignment via `plagiarism_enabled` boolean on `encoach.assignment`
- Configurable max submissions for plagiarism check via `plagiarism_max_checks` integer

---

## 31. Official Exam Access

**Module enhancement:** `encoach_exam` (add official access modes)

### 31.1 Model Extensions

Add to `encoach.exam`:

| Field | Type | Description |
|-------|------|-------------|
| `is_exercise` | Boolean | Exercise (practice, no journey tracking) vs Exam |
| `is_official` | Boolean | Official exam flag |
| `access_mode` | Selection | `normal`, `link`, `landing_page`, `separate_login` |
| `access_token` | Char | Unique access token (auto-generated) |
| `access_code` | Char | Exam access code |
| `activation_start` | Datetime | Exam start time |
| `activation_end` | Datetime | Exam end time |
| `generate_unique_per_student` | Boolean | AI generates unique variant per student |
| `reminder_count` | Integer | Number of reminders to send |
| `reminder_frequency` | Selection | `once`, `daily`, `custom` |

### 31.2 API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/exam/{id}/generate-link` | JWT (teacher) | Generate unique access token/URL |
| `GET` | `/api/exam/access/{token}` | Public | Access exam via token (validates token, returns exam info) |
| `POST` | `/api/exam/official-login` | Public | Login with exam code + credentials |
| `POST` | `/api/exam/{id}/suspend` | JWT (teacher) | Suspend exam |
| `POST` | `/api/exam/{id}/revoke` | JWT (teacher) | Revoke exam access |

---

## 32. Non-Functional Requirements

### 32.1 Performance

| ID | Requirement |
|----|-------------|
| NFR-PERF-01 | API response time < 500ms for CRUD endpoints |
| NFR-PERF-02 | AI endpoints may take up to 30s (use async job for long operations) |
| NFR-PERF-03 | Pagination: default `size=20`, max `size=100` |

### 32.2 Security

| ID | Requirement |
|----|-------------|
| NFR-SEC-01 | JWT tokens expire after 24 hours |
| NFR-SEC-02 | All endpoints validate entity-scoped permissions |
| NFR-SEC-03 | Rate limiting on AI endpoints (configurable) |
| NFR-SEC-04 | CORS configured for frontend origin |
| NFR-SEC-05 | File upload size limit: 50MB |
| NFR-SEC-06 | API keys stored in `ir.config_parameter`, never in code |

### 32.3 Scalability

| ID | Requirement |
|----|-------------|
| NFR-SCALE-01 | Support 10,000+ concurrent students |
| NFR-SCALE-02 | Support 50+ subjects, 500+ topics per subject, 1000+ resources per subject |
| NFR-SCALE-03 | Database indices on all foreign keys and frequently filtered fields |

### 32.4 Reliability

| ID | Requirement |
|----|-------------|
| NFR-REL-01 | AI service failures return graceful error (never 500) |
| NFR-REL-02 | Webhook processing is idempotent |
| NFR-REL-03 | Soft-delete for resources (preserve completion records) |

### 32.5 API Conventions

| Convention | Detail |
|-----------|--------|
| Date format | ISO 8601 (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`) |
| Pagination | `?page=1&size=20` returns `{ "items": [...], "total": N, "page": 1, "size": 20 }` |
| Error shape | `{ "error": "message", "code": "ERROR_CODE" }` with HTTP status |
| ID fields | Many2one serialized as `{field}_id` (int) + `{field}_name` (str) |
| List fields | Many2many serialized as `{field}_ids` (int[]) + `{field}_names` (str[]) |
| Binary fields | Base64-encoded strings or separate download endpoint |
| Auth header | `Authorization: Bearer <jwt_token>` |

---

## 33. Implementation Status

All modules have been implemented and deployed. The original priority ordering is preserved for reference.

| Priority | Modules | Status |
|----------|---------|--------|
| **P0** | `encoach_core`, `encoach_api` (auth, users, entities, permissions) | **DONE** |
| **P0** | `openeducat_core` (port to v19), `encoach_lms_api` | **DONE** |
| **P1** | `encoach_taxonomy`, `encoach_adaptive`, `encoach_adaptive_api`, `encoach_adaptive_ai` | **DONE** |
| **P1** | `encoach_exam`, `encoach_ai_generation`, `encoach_ai_grading` | **DONE** |
| **P1** | `encoach_resources` | **DONE** |
| **P2** | `encoach_courseware` (chapters, materials, progress, AI workbench) | **DONE** |
| **P2** | `encoach_communication` (discussions, announcements, messaging) | **DONE** |
| **P2** | `openeducat_timetable`, `openeducat_attendance`, `openeducat_classroom` | **DONE** |
| **P2** | `openeducat_admission` + admission endpoints | **DONE** |
| **P2** | Academic years/terms, departments endpoints | **DONE** |
| **P2** | `encoach_ai`, `encoach_ai_media` | **DONE** |
| **P2** | `encoach_assignment`, `encoach_classroom` | **DONE** |
| **P3** | `encoach_notification` (rules, email delivery, preferences) | **DONE** |
| **P3** | Enhanced approval workflows (stages, escalation, bypass) | **DONE** |
| **P3** | Plagiarism detection (GPTZero integration) | **DONE** |
| **P3** | Official exam access modes (link, landing, separate login) | **DONE** |
| **P3** | Late submission handling, extension requests | **DONE** |
| **P3** | `openeducat_exam` + institutional exam endpoints | **DONE** |
| **P3** | `openeducat_assignment` + submission endpoints | **DONE** |
| **P3** | Subject registration endpoints | **DONE** |
| **P3** | `encoach_training`, analytics endpoints | **DONE** |
| **P3** | `encoach_sis`, `encoach_branding` | **DONE** |
| **P4** | `encoach_faq` (categories, items, role-filtered) | **DONE** |
| **P4** | `encoach_subscription` (Stripe, PayPal, Paymob) | **DONE** |
| **P4** | `encoach_ticket` | **DONE** |
| **P4** | Storage | **DONE** |

---

## 34. Beyond-SRS Features (Implemented)

The developer implemented the following features beyond the original SRS scope. These leverage OpenEduCat Enterprise modules and add significant institutional management capabilities.

### 34.1 Student Leave Management

**Module:** `openeducat_student_leave_enterprise` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/student_leave.py`
**Frontend:** `student-leave.service.ts` | `AdminStudentLeave.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/student-leaves` | List leave requests |
| `POST` | `/api/student-leaves` | Create leave request |
| `PATCH` | `/api/student-leaves/{id}` | Update leave request |
| `POST` | `/api/student-leaves/{id}/approve` | Approve request |
| `POST` | `/api/student-leaves/{id}/reject` | Reject request |
| `GET` | `/api/student-leave-types` | List leave types |
| `POST` | `/api/student-leave-types` | Create leave type |

### 34.2 Fees Management

**Module:** `openeducat_fees` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/fees.py`
**Frontend:** `fees.service.ts` | `AdminFees.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/fees-plans` | List fee plans |
| `GET` | `/api/fees-plans/{id}` | Fee plan detail |
| `GET` | `/api/student-fees` | Student fee details |
| `GET` | `/api/fees-terms` | Fee terms |

### 34.3 Lessons Management

**Module:** `openeducat_lesson` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/lessons.py`
**Frontend:** `lesson.service.ts` | `AdminLessons.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/lessons` | List lessons |
| `POST` | `/api/lessons` | Create lesson |
| `PATCH` | `/api/lessons/{id}` | Update lesson |
| `DELETE` | `/api/lessons/{id}` | Delete lesson |

### 34.4 Gradebook and Grading Assignments

**Module:** `openeducat_grading` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/gradebook.py`
**Frontend:** `gradebook.service.ts` | `AdminGradebook.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/gradebooks` | List gradebooks |
| `GET` | `/api/gradebook-lines` | Gradebook lines |
| `GET` | `/api/grading-assignments` | List grading assignments |
| `POST` | `/api/grading-assignments` | Create grading assignment |
| `PATCH` | `/api/grading-assignments/{id}` | Update grading assignment |
| `DELETE` | `/api/grading-assignments/{id}` | Delete grading assignment |

### 34.5 Student Progress Tracking

**Module:** `openeducat_student_progress_enterprise` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/student_progress.py`
**Frontend:** `student-progress.service.ts` | `AdminStudentProgress.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/student-progress` | List student progression records |

### 34.6 Library Management

**Module:** `openeducat_library` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/library.py`
**Frontend:** `library.service.ts` | `AdminLibrary.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/library/media` | List media items |
| `POST` | `/api/library/media` | Create media item |
| `DELETE` | `/api/library/media/{id}` | Delete media item |
| `GET` | `/api/library/movements` | List movements |
| `POST` | `/api/library/movements` | Create movement (issue) |
| `PATCH` | `/api/library/movements/{id}` | Update movement |
| `POST` | `/api/library/movements/{id}/return` | Return media |
| `GET` | `/api/library/cards` | List library cards |
| `POST` | `/api/library/cards` | Create library card |

### 34.7 Activity Management

**Module:** `openeducat_activity` (via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/activities.py`
**Frontend:** `activity.service.ts` | `AdminActivities.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/activities` | List activities |
| `POST` | `/api/activities` | Create activity |
| `PATCH` | `/api/activities/{id}` | Update activity |
| `DELETE` | `/api/activities/{id}` | Delete activity |
| `GET` | `/api/activity-types` | List activity types |
| `POST` | `/api/activity-types` | Create activity type |
| `DELETE` | `/api/activity-types/{id}` | Delete activity type |

### 34.8 Facility and Asset Management

**Module:** `openeducat_facility` (extended via `encoach_lms_api`)
**Controller:** `encoach_lms_api/controllers/facilities.py`
**Frontend:** `facility.service.ts` | `AdminFacilities.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/facilities` | List facilities |
| `POST` | `/api/facilities` | Create facility |
| `PATCH` | `/api/facilities/{id}` | Update facility |
| `DELETE` | `/api/facilities/{id}` | Delete facility |
| `GET` | `/api/assets` | List assets |
| `POST` | `/api/assets` | Create asset |
| `DELETE` | `/api/assets/{id}` | Delete asset |

### 34.9 Roles CRUD, User Role Assignment, and Authority Matrix

**Module:** `encoach_core` (extended)
**Controller:** `encoach_api/controllers/roles.py`
**Frontend:** `entities.service.ts` | `RolesPermissions.tsx`, `UserRoles.tsx`, `AuthorityMatrix.tsx`

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/roles` | List all roles |
| `POST` | `/api/roles` | Create role |
| `GET` | `/api/roles/{id}` | Get role detail |
| `PATCH` | `/api/roles/{id}` | Update role |
| `DELETE` | `/api/roles/{id}` | Delete role |
| `PATCH` | `/api/roles/{id}/permissions` | Update role permissions |
| `GET` | `/api/permissions` | List all permissions |
| `POST` | `/api/permissions` | Create permission |
| `DELETE` | `/api/permissions/{id}` | Delete permission |
| `GET` | `/api/users/with-roles` | List users with roles |
| `GET/PATCH` | `/api/users/{id}/roles` | Get/update user roles |
| `POST` | `/api/users/{id}/roles/toggle` | Toggle role for user |
| `GET` | `/api/authority-matrix` | Get role-permission matrix |
| `POST` | `/api/authority-matrix/toggle` | Toggle a matrix cell |
| `GET` | `/api/user-authority-matrix` | Get user-level authority |

---

## Appendix A: Complete Endpoint Summary (Verified Against Deployment)

Total API endpoints: **~377** (deployed across 4 controller packages)

| Controller Package | Route Count | Service Areas |
|-------------------|-------------|---------------|
| `encoach_api` | ~120 | Auth, users, entities, roles, exams, assignments, classrooms, storage, stats, subscriptions, tickets, approvals, training, generation, AI analytics |
| `encoach_lms_api` | ~209 | Courses, students, teachers, batches, timetable, attendance, grades, departments, academic, admissions, subject registration, institutional exams, course assignments, student leave, fees, lessons, gradebook, student progress, library, activities, facilities, notifications, FAQ, courseware, plagiarism, communication, classrooms |
| `encoach_adaptive_api` | ~41 | Taxonomy (subjects, domains, topics), diagnostic, proficiency, learning plans, content, coaching |
| `encoach_resources` | ~7 | Learning resources (CRUD, download, complete, rate) |

**Breakdown by Service Area:**

| Service Area | Endpoint Count | Status |
|-------------|---------------|--------|
| Auth & Users | 10 | Deployed |
| Entities & Permissions | 9 | Deployed |
| Roles CRUD & Authority Matrix | 12 | **Deployed (Beyond SRS)** |
| LMS (Courses, Batches, Timetable, Attendance, Grades) | 16 | Deployed |
| Academic Years, Terms, Departments | 14 | Deployed |
| Admissions | 12 | Deployed |
| Subject Registration | 6 | Deployed |
| EnCoach Exams (AI) | 15 | Deployed |
| Institutional Exams | 21 | Deployed |
| EnCoach Assignments | 7 | Deployed |
| Course Assignments & Submissions | 11 | Deployed |
| Classrooms & Groups | 7 | Deployed |
| Taxonomy | 16 | Deployed |
| Adaptive Learning | 17 | Deployed |
| Resources | 7 | Deployed |
| AI Coaching | 6 | Deployed |
| AI Evaluation | 4 | Deployed |
| AI Generation | 2 | Deployed |
| AI Media | 3 | Deployed |
| AI Analytics | 6 | Deployed |
| Training | 3 | Deployed |
| Statistics | 4 | Deployed |
| Subscriptions & Payments | 8 + 3 webhooks | Deployed |
| Tickets | 4 | Deployed |
| Storage | 2 | Deployed |
| Approval Workflows (Enhanced) | 11 | Deployed |
| Courseware (Chapters + Materials + Workbench) | 23 | Deployed |
| Discussion Boards + Posts | 9 | Deployed |
| Announcements | 5 | Deployed |
| Messaging | 6 | Deployed |
| Notifications + Rules + Preferences | 10 | Deployed |
| FAQ (Categories + Items) | 8 | Deployed |
| Plagiarism Detection | 3 | Deployed |
| Official Exam Access | 5 | Deployed |
| Student Leave Management | 7 | **Deployed (Beyond SRS)** |
| Fees Management | 4 | **Deployed (Beyond SRS)** |
| Lessons Management | 4 | **Deployed (Beyond SRS)** |
| Gradebook & Grading | 6 | **Deployed (Beyond SRS)** |
| Student Progress | 1 | **Deployed (Beyond SRS)** |
| Library Management | 9 | **Deployed (Beyond SRS)** |
| Activity Management | 7 | **Deployed (Beyond SRS)** |
| Facility & Asset Management | 7 | **Deployed (Beyond SRS)** |

---

## Appendix B: Model Relationship Diagram

```
res.users ──extends──> encoach.user
    │                      │
    │                      ├── entity_ids ──> encoach.entity
    │                      │                     └── role_ids ──> encoach.role
    │                      │                                        └── permission_ids ──> encoach.permission
    │                      │
    │                      ├── op_student_id ──> op.student ──> op.student.course ──> op.course
    │                      │                                                            ├── op.batch
    │                      │                                                            ├── op.subject
    │                      │                                                            └── op.department
    │                      │
    │                      └── op_faculty_id ──> op.faculty
    │
    ├── encoach.exam ──> encoach.subject (taxonomy)
    │                        ├── encoach.domain
    │                        │       └── encoach.topic
    │                        │               ├── encoach.learning.objective
    │                        │               ├── encoach.proficiency
    │                        │               └── encoach.resource
    │                        │
    │                        └── encoach.learning.plan
    │                                └── encoach.learning.plan.item ──> encoach.topic
    │
    ├── op.academic.year ──> op.academic.term
    │
    ├── op.admission.register ──> op.admission ──> op.student
    │
    ├── op.exam.session ──> op.exam ──> op.exam.attendees
    │                                       └── op.marksheet.register ──> op.marksheet.line
    │
    ├── op.assignment ──> op.assignment.sub.line (submissions)
    │
    ├── encoach.course.chapter ──> op.course
    │       ├── encoach.chapter.material
    │       ├── encoach.chapter.progress ──> res.users
    │       └── encoach.topic (bridge to adaptive)
    │
    ├── encoach.discussion.board ──> op.course / op.batch / encoach.course.chapter
    │       └── encoach.discussion.post (self-referencing for threads)
    │
    ├── encoach.announcement ──> op.course / op.batch
    │
    ├── encoach.message ──> res.users (sender/recipient)
    │
    ├── encoach.notification ──> res.users
    │       └── encoach.notification.rule (event triggers)
    │
    ├── encoach.faq.category ──> encoach.faq.item
    │
    ├── encoach.approval.workflow ──> encoach.approval.stage
    │       └── encoach.approval.request
    │
    └── encoach.plagiarism.report ──> submission
```

---

*This SRS is the definitive backend specification for the EnCoach platform on Odoo 19. All ~377 REST API endpoints, 41 Odoo modules (14 OpenEduCat + 27 custom), and all AI service integrations have been implemented and deployed to staging at `http://5.189.151.117:8069`. All previous backend SRS documents (`ODOO_BACKEND_SRS_v3.md`, `ODOO_MIGRATION_SRS_v2.md`, `ODOO_MIGRATION_SRS.md`) are superseded by this document.*
