# pyrefly: ignore [missing-import] from odoo import api, fields, models # pyrefly: ignore [missing-import] from odoo.exceptions import ValidationError class EducationAttendanceSession(models.Model): _name = 'education.attendance.session' _description = 'Attendance Session' _inherit = ['mail.thread', 'mail.activity.mixin'] _order = 'attendance_date desc, period asc, id desc' name = fields.Char(string='Session Title', required=True, tracking=True) campus_id = fields.Many2one('education.campus', string='Campus', required=True, index=True) department_id = fields.Many2one('education.department', string='Department', required=True, index=True) program_id = fields.Many2one('education.program', string='Program', required=True, index=True) academic_year_id = fields.Many2one('education.academic.year', string='Academic Year', required=True, index=True) academic_term_id = fields.Many2one('education.academic.term', string='Academic Term', required=True, index=True) academic_level_id = fields.Many2one('education.academic.level', string='Academic Level', required=True, index=True) attendance_date = fields.Date(string='Attendance Date', required=True, default=fields.Date.context_today, index=True, tracking=True) period = fields.Selection([ ('period_1', 'Period 1'), ('period_2', 'Period 2'), ('period_3', 'Period 3'), ('period_4', 'Period 4'), ('period_5', 'Period 5'), ('period_6', 'Period 6'), ('period_7', 'Period 7'), ('period_8', 'Period 8') ], string='Period', required=True, default='period_1', tracking=True) faculty_id = fields.Many2one('res.users', string='Faculty', required=True, index=True, default=lambda self: self.env.user, tracking=True) state = fields.Selection([ ('draft', 'Draft'), ('open', 'Open'), ('completed', 'Completed'), ('cancelled', 'Cancelled') ], string='Status', default='draft', required=True, index=True, tracking=True) line_ids = fields.One2many('education.attendance.line', 'session_id', string='Attendance Lines', copy=True) active = fields.Boolean(default=True) company_id = fields.Many2one( 'res.company', string='Company', required=True, default=lambda self: self.env.company, index=True, ) _uniq_session_period = models.Constraint('unique(attendance_date, period, program_id, academic_level_id, company_id)', 'An attendance session has already been created for this period, program, and academic level on this date.') def action_open(self): for rec in self: if rec.state != 'draft': continue # Auto-populate attendance lines with active students students = self.env['education.student'].search([ ('campus_id', '=', rec.campus_id.id), ('department_id', '=', rec.department_id.id), ('program_id', '=', rec.program_id.id), ('academic_year_id', '=', rec.academic_year_id.id), ('academic_term_id', '=', rec.academic_term_id.id), ('level_id', '=', rec.academic_level_id.id), ('status', '=', 'active'), ]) if not students: raise ValidationError("No active students found matching the selected academic criteria.") # Create lines lines_vals = [] for student in students: lines_vals.append({ 'session_id': rec.id, 'student_id': student.id, 'status': 'present', 'company_id': rec.company_id.id, }) self.env['education.attendance.line'].create(lines_vals) rec.state = 'open' def action_complete(self): for rec in self: if rec.state != 'open': continue if not rec.line_ids: raise ValidationError("Cannot complete session without any student lines.") # Copy to permanent history table history_vals = [] for line in rec.line_ids: history_vals.append({ 'student_id': line.student_id.id, 'date': rec.attendance_date, 'session_id': rec.id, 'status': line.status, 'faculty_id': rec.faculty_id.id, 'remarks': line.remarks, 'company_id': rec.company_id.id, }) self.env['education.attendance'].create(history_vals) rec.state = 'completed' # Invalidate student attendance cache students = rec.line_ids.mapped('student_id') students.invalidate_recordset() def action_cancel(self): for rec in self: if rec.state == 'completed': # Delete permanent history history = self.env['education.attendance'].search([('session_id', '=', rec.id)]) history.unlink() # Delete lines rec.line_ids.unlink() rec.state = 'cancelled' def action_draft(self): for rec in self: # Delete permanent history if completed if rec.state == 'completed': history = self.env['education.attendance'].search([('session_id', '=', rec.id)]) history.unlink() # Delete lines rec.line_ids.unlink() rec.state = 'draft' # Bulk marking actions def action_bulk_present(self): self.ensure_one() if self.state != 'open': raise ValidationError("Bulk marking is only allowed when the session is Open.") self.line_ids.write({'status': 'present'}) def action_bulk_absent(self): self.ensure_one() if self.state != 'open': raise ValidationError("Bulk marking is only allowed when the session is Open.") self.line_ids.write({'status': 'absent'}) def action_bulk_late(self): self.ensure_one() if self.state != 'open': raise ValidationError("Bulk marking is only allowed when the session is Open.") self.line_ids.write({'status': 'late'}) def action_bulk_medical(self): self.ensure_one() if self.state != 'open': raise ValidationError("Bulk marking is only allowed when the session is Open.") self.line_ids.write({'status': 'medical_leave'}) def action_bulk_duty(self): self.ensure_one() if self.state != 'open': raise ValidationError("Bulk marking is only allowed when the session is Open.") self.line_ids.write({'status': 'on_duty'})