from odoo import fields, models class McStudent(models.Model): _inherit = "mc.student" def _get_report_card_term(self): """The term a report card prints for: the most recent term this student actually has marks in, or - nothing graded yet - whichever term's date range covers today. Marks win over the calendar deliberately: a report card is generated to show a term's *results*, typically reviewed after that term's exams are done and marks entered (which is often during the *next* term, not the graded term itself) - resolving strictly by today's date would show an empty current term instead of the graded one. Lets "Print Report Card" work with no extra picker on the common path, per Demo Scene 6 ("As Principal, generate Aditya's report card" - one click, no term-selection wizard). """ self.ensure_one() enrollment = self._get_report_card_enrollment() if not enrollment: return self.env["mc.academic.term"] marks = self.env["mc.mark"].search([("student_id", "=", self.id)]) terms_with_marks = marks.exam_id.term_id.filtered( lambda t: t.year_id == enrollment.year_id ) if terms_with_marks: return terms_with_marks.sorted("date_start", reverse=True)[:1] today = fields.Date.context_today(self) return self.env["mc.academic.term"].search([ ("year_id", "=", enrollment.year_id.id), ("date_start", "<=", today), ("date_end", ">=", today), ], limit=1) def _get_report_card_enrollment(self): self.ensure_one() return self.env["mc.enrollment"].search([ ("student_id", "=", self.id), ("state", "=", "active"), ], limit=1) def _get_report_card_marks(self, term): self.ensure_one() return self.env["mc.mark"].search([ ("student_id", "=", self.id), ("exam_id.term_id", "=", term.id), ]) def _get_report_card_attendance_summary(self, term): """Attendance summary is optional: mc_education_exam does not depend on mc_education_attendance (each mc_education_* module installs independently, CLAUDE.md sec 1.4), so this degrades to None rather than failing when attendance isn't installed. """ self.ensure_one() try: attendance_model = self.env["mc.attendance"] except KeyError: return None records = attendance_model.search([ ("student_id", "=", self.id), ("date", ">=", term.date_start), ("date", "<=", term.date_end), ]) total = len(records) present = len(records.filtered(lambda a: a.state in ("present", "late"))) return { "total": total, "present": present, "percentage": (present / total * 100) if total else 0.0, }