Session 3-A - core models: community.school.term, .level (admin-defined, so the same module fits a Grade 1-12 school or a Beginner-Advanced language school with no code change), .class (auto-creates a linked slide.channel for LMS glue), .student, .enrollment, and .attendance, plus is_teacher on res.partner. Class enrolled_count and enrollment attendance_rate are stored/non-stored computes driven by the enrollment/attendance one2many chains. Session 3-B - teacher attendance + at-risk reporting: a portal page at /school/attendance (auth='user', scoped to classes where teacher_id matches the logged-in user's partner - works whether the teacher is an internal or portal user) with a roster and batch save, built as a v19 Interaction (same pattern as event_qr_ticketing's check-in page). Marking a student absent queues a configurable notice to the parent partner. Enrollment.is_at_risk flags students below a configurable attendance threshold (School settings page, same <app>/<block>/<setting> pattern as community_membership), plus an admin pivot attendance report and an At-Risk Students list. Verified against a live Odoo 19 + Postgres 16 container: 14/14 automated tests pass (class/slide-channel creation, enrolled_count tracking incl. withdrawal, attendance_rate and at-risk computation, unique enrollment+date constraint, absence email queuing), plus a full manual live run - created a term/level/class/student/enrollment via JSON-RPC, loaded the actual /school/attendance page as the teacher, POSTed a batch save marking the student absent, and confirmed both the attendance record and the queued "Absence notice" mail.mail record. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
64 lines
3.5 KiB
XML
64 lines
3.5 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<template id="portal_no_classes" name="School Attendance: No Classes">
|
|
<t t-call="website.layout">
|
|
<div class="container" style="max-width: 480px; margin-top: 60px;">
|
|
<p class="alert alert-info">You are not assigned as a teacher to any class.</p>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="portal_attendance" name="School Attendance">
|
|
<t t-call="website.layout">
|
|
<div class="container o_school_attendance" style="max-width: 640px; margin-top: 24px; margin-bottom: 60px;"
|
|
t-att-data-class-id="selected_class.id" t-att-data-date="selected_date">
|
|
<h2>Attendance</h2>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<label class="form-label">Class</label>
|
|
<select class="form-select o_attendance_class_select">
|
|
<t t-foreach="classes" t-as="klass">
|
|
<option t-att-value="klass.id" t-att-selected="'selected' if klass.id == selected_class.id else None" t-out="klass.name"/>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label">Date</label>
|
|
<input type="date" class="form-control o_attendance_date_input" t-att-value="selected_date"/>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="table">
|
|
<thead>
|
|
<tr><th>Student</th><th>Status</th><th>Notes</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<t t-foreach="roster" t-as="row">
|
|
<tr t-att-data-enrollment-id="row['enrollment'].id">
|
|
<td t-out="row['enrollment'].student_id.partner_id.name"/>
|
|
<td>
|
|
<select class="form-select form-select-sm o_attendance_state_select">
|
|
<t t-set="current_state" t-value="row['attendance'].state if row['attendance'] else 'present'"/>
|
|
<option value="present" t-att-selected="'selected' if current_state == 'present' else None">Present</option>
|
|
<option value="absent" t-att-selected="'selected' if current_state == 'absent' else None">Absent</option>
|
|
<option value="late" t-att-selected="'selected' if current_state == 'late' else None">Late</option>
|
|
<option value="excused" t-att-selected="'selected' if current_state == 'excused' else None">Excused</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input type="text" class="form-control form-control-sm o_attendance_notes_input"
|
|
t-att-value="row['attendance'].notes if row['attendance'] else ''"/>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
|
|
<button class="btn btn-primary o_attendance_save_btn" type="button">Save Attendance</button>
|
|
<div class="o_attendance_result mt-2" role="status"></div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
</odoo>
|