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>
232 lines
9.6 KiB
XML
232 lines
9.6 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<!-- Term -->
|
|
<record id="view_school_term_list" model="ir.ui.view">
|
|
<field name="name">community.school.term.list</field>
|
|
<field name="model">community.school.term</field>
|
|
<field name="arch" type="xml">
|
|
<list string="School Terms">
|
|
<field name="name"/>
|
|
<field name="start_date"/>
|
|
<field name="end_date"/>
|
|
<field name="registration_open"/>
|
|
<field name="state"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="view_school_term_form" model="ir.ui.view">
|
|
<field name="name">community.school.term.form</field>
|
|
<field name="model">community.school.term</field>
|
|
<field name="arch" type="xml">
|
|
<form string="School Term">
|
|
<sheet>
|
|
<div class="oe_title"><h1><field name="name"/></h1></div>
|
|
<group>
|
|
<group>
|
|
<field name="start_date"/>
|
|
<field name="end_date"/>
|
|
<field name="state"/>
|
|
</group>
|
|
<group>
|
|
<field name="registration_open"/>
|
|
<field name="registration_deadline"/>
|
|
</group>
|
|
</group>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_term" model="ir.actions.act_window">
|
|
<field name="name">Terms</field>
|
|
<field name="res_model">community.school.term</field>
|
|
<field name="view_mode">list,form</field>
|
|
</record>
|
|
|
|
<!-- Level -->
|
|
<record id="view_school_level_list" model="ir.ui.view">
|
|
<field name="name">community.school.level.list</field>
|
|
<field name="model">community.school.level</field>
|
|
<field name="arch" type="xml">
|
|
<list string="School Levels" editable="bottom">
|
|
<field name="sequence" widget="handle"/>
|
|
<field name="name"/>
|
|
<field name="code"/>
|
|
<field name="min_age"/>
|
|
<field name="max_age"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_level" model="ir.actions.act_window">
|
|
<field name="name">Levels</field>
|
|
<field name="res_model">community.school.level</field>
|
|
<field name="view_mode">list</field>
|
|
</record>
|
|
|
|
<!-- Class -->
|
|
<record id="view_school_class_list" model="ir.ui.view">
|
|
<field name="name">community.school.class.list</field>
|
|
<field name="model">community.school.class</field>
|
|
<field name="arch" type="xml">
|
|
<list string="School Classes">
|
|
<field name="name"/>
|
|
<field name="teacher_id"/>
|
|
<field name="weekday"/>
|
|
<field name="enrolled_count"/>
|
|
<field name="max_students"/>
|
|
<field name="state"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="view_school_class_form" model="ir.ui.view">
|
|
<field name="name">community.school.class.form</field>
|
|
<field name="model">community.school.class</field>
|
|
<field name="arch" type="xml">
|
|
<form string="School Class">
|
|
<sheet>
|
|
<div class="oe_title"><h1><field name="name" readonly="1"/></h1></div>
|
|
<group>
|
|
<group>
|
|
<field name="level_id"/>
|
|
<field name="term_id"/>
|
|
<field name="teacher_id"/>
|
|
<field name="state"/>
|
|
</group>
|
|
<group>
|
|
<field name="weekday"/>
|
|
<field name="start_time" widget="float_time"/>
|
|
<field name="end_time" widget="float_time"/>
|
|
<field name="location"/>
|
|
<field name="max_students"/>
|
|
<field name="enrolled_count"/>
|
|
<field name="slide_channel_id" readonly="1"/>
|
|
</group>
|
|
</group>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_class" model="ir.actions.act_window">
|
|
<field name="name">Classes</field>
|
|
<field name="res_model">community.school.class</field>
|
|
<field name="view_mode">list,form</field>
|
|
</record>
|
|
|
|
<!-- Student -->
|
|
<record id="view_school_student_list" model="ir.ui.view">
|
|
<field name="name">community.school.student.list</field>
|
|
<field name="model">community.school.student</field>
|
|
<field name="arch" type="xml">
|
|
<list string="Students">
|
|
<field name="partner_id"/>
|
|
<field name="parent_partner_id"/>
|
|
<field name="age"/>
|
|
<field name="proficiency"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="view_school_student_form" model="ir.ui.view">
|
|
<field name="name">community.school.student.form</field>
|
|
<field name="model">community.school.student</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Student">
|
|
<sheet>
|
|
<div class="oe_title"><h1><field name="partner_id"/></h1></div>
|
|
<group>
|
|
<group>
|
|
<field name="parent_partner_id"/>
|
|
<field name="date_of_birth"/>
|
|
<field name="age" readonly="1"/>
|
|
<field name="proficiency"/>
|
|
<field name="grade_ref"/>
|
|
</group>
|
|
<group>
|
|
<field name="emergency_contact_name"/>
|
|
<field name="emergency_contact_phone"/>
|
|
</group>
|
|
</group>
|
|
<group string="Health Notes">
|
|
<field name="health_notes" nolabel="1"/>
|
|
</group>
|
|
<notebook>
|
|
<page string="Enrollments" name="enrollments">
|
|
<field name="enrollment_ids">
|
|
<list>
|
|
<field name="class_id"/>
|
|
<field name="term_id"/>
|
|
<field name="state"/>
|
|
<field name="payment_state"/>
|
|
<field name="attendance_rate" widget="percentage"/>
|
|
</list>
|
|
</field>
|
|
</page>
|
|
</notebook>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_student" model="ir.actions.act_window">
|
|
<field name="name">Students</field>
|
|
<field name="res_model">community.school.student</field>
|
|
<field name="view_mode">list,form</field>
|
|
</record>
|
|
|
|
<!-- Enrollment -->
|
|
<record id="view_school_enrollment_list" model="ir.ui.view">
|
|
<field name="name">community.school.enrollment.list</field>
|
|
<field name="model">community.school.enrollment</field>
|
|
<field name="arch" type="xml">
|
|
<list string="Enrollments">
|
|
<field name="student_id"/>
|
|
<field name="class_id"/>
|
|
<field name="term_id"/>
|
|
<field name="state"/>
|
|
<field name="payment_state"/>
|
|
<field name="attendance_rate" widget="percentage"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_enrollment" model="ir.actions.act_window">
|
|
<field name="name">Enrollments</field>
|
|
<field name="res_model">community.school.enrollment</field>
|
|
<field name="view_mode">list,form</field>
|
|
</record>
|
|
|
|
<record id="action_school_enrollment_at_risk" model="ir.actions.act_window">
|
|
<field name="name">At-Risk Students</field>
|
|
<field name="res_model">community.school.enrollment</field>
|
|
<field name="view_mode">list,form</field>
|
|
<field name="domain">[('is_at_risk', '=', True)]</field>
|
|
</record>
|
|
|
|
<!-- Attendance pivot report -->
|
|
<record id="view_school_attendance_pivot" model="ir.ui.view">
|
|
<field name="name">community.school.attendance.pivot</field>
|
|
<field name="model">community.school.attendance</field>
|
|
<field name="arch" type="xml">
|
|
<pivot string="Attendance">
|
|
<field name="class_id" type="row"/>
|
|
<field name="date" type="col" interval="week"/>
|
|
<field name="state" type="row"/>
|
|
</pivot>
|
|
</field>
|
|
</record>
|
|
<record id="view_school_attendance_list" model="ir.ui.view">
|
|
<field name="name">community.school.attendance.list</field>
|
|
<field name="model">community.school.attendance</field>
|
|
<field name="arch" type="xml">
|
|
<list string="Attendance">
|
|
<field name="date"/>
|
|
<field name="class_id"/>
|
|
<field name="enrollment_id"/>
|
|
<field name="state"/>
|
|
<field name="notes"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
<record id="action_school_attendance_report" model="ir.actions.act_window">
|
|
<field name="name">Attendance Report</field>
|
|
<field name="res_model">community.school.attendance</field>
|
|
<field name="view_mode">pivot,list</field>
|
|
</record>
|
|
</odoo>
|