from psycopg2 import IntegrityError from odoo.tests.common import TransactionCase from odoo.tools import mute_logger class TestAttendance(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.year = cls.env["mc.academic.year"].create({ "name": "TEST-ATT-2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", }) cls.program = cls.env["mc.program"].create({ "name": "TEST ATT Program", "code": "TEST-ATT-P1", "sequence_no": 1, "display_label": "Test Grade", }) cls.batch = cls.env["mc.batch"].create({ "name": "TEST ATT Batch", "program_id": cls.program.id, "year_id": cls.year.id, }) partner1 = cls.env["res.partner"].create({"name": "ATT Student One"}) cls.student1 = cls.env["mc.student"].create({ "partner_id": partner1.id, "name": "ATT Student One", }) partner2 = cls.env["res.partner"].create({"name": "ATT Student Two"}) cls.student2 = cls.env["mc.student"].create({ "partner_id": partner2.id, "name": "ATT Student Two", }) cls.env["mc.enrollment"].create({ "student_id": cls.student1.id, "program_id": cls.program.id, "batch_id": cls.batch.id, "year_id": cls.year.id, "state": "active", }) cls.env["mc.enrollment"].create({ "student_id": cls.student2.id, "program_id": cls.program.id, "batch_id": cls.batch.id, "year_id": cls.year.id, "state": "active", }) @mute_logger("odoo.sql_db") def test_duplicate_student_date_session_rejected(self): self.env["mc.attendance"].create({ "student_id": self.student1.id, "batch_id": self.batch.id, "date": "2026-09-01", "session": "Daily", "state": "present", }) with self.assertRaises(IntegrityError): with self.cr.savepoint(): self.env["mc.attendance"].create({ "student_id": self.student1.id, "batch_id": self.batch.id, "date": "2026-09-01", "session": "Daily", "state": "absent", }) def test_different_session_same_day_is_allowed(self): self.env["mc.attendance"].create({ "student_id": self.student1.id, "batch_id": self.batch.id, "date": "2026-09-01", "session": "Daily", "state": "present", }) # Should not raise: different session on the same date/student. self.env["mc.attendance"].create({ "student_id": self.student1.id, "batch_id": self.batch.id, "date": "2026-09-01", "session": "Period 2", "state": "absent", }) def test_wizard_loads_active_enrollments_as_roster(self): wizard = self.env["mc.attendance.bulk.wizard"].create({ "batch_id": self.batch.id, "date": "2026-09-02", "session": "Daily", }) wizard._onchange_load_roster() self.assertEqual(set(wizard.line_ids.mapped("student_id")), {self.student1, self.student2}) self.assertTrue(all(line.state == "present" for line in wizard.line_ids)) def test_wizard_submit_creates_attendance(self): wizard = self.env["mc.attendance.bulk.wizard"].create({ "batch_id": self.batch.id, "date": "2026-09-03", "session": "Daily", }) wizard._onchange_load_roster() wizard.line_ids.filtered(lambda l: l.student_id == self.student2).state = "absent" wizard.action_submit() records = self.env["mc.attendance"].search([ ("batch_id", "=", self.batch.id), ("date", "=", "2026-09-03"), ]) self.assertEqual(len(records), 2) absent = records.filtered(lambda a: a.student_id == self.student2) self.assertEqual(absent.state, "absent") def test_resubmitting_wizard_is_idempotent(self): # First submission: mark everyone present. wizard1 = self.env["mc.attendance.bulk.wizard"].create({ "batch_id": self.batch.id, "date": "2026-09-04", "session": "Daily", }) wizard1._onchange_load_roster() wizard1.action_submit() self.assertEqual( self.env["mc.attendance"].search_count([ ("batch_id", "=", self.batch.id), ("date", "=", "2026-09-04"), ]), 2, ) # Re-open and correct one student to late - must update the same # two rows, never create new ones. wizard2 = self.env["mc.attendance.bulk.wizard"].create({ "batch_id": self.batch.id, "date": "2026-09-04", "session": "Daily", }) wizard2._onchange_load_roster() wizard2.line_ids.filtered(lambda l: l.student_id == self.student1).state = "late" wizard2.action_submit() records = self.env["mc.attendance"].search([ ("batch_id", "=", self.batch.id), ("date", "=", "2026-09-04"), ]) self.assertEqual(len(records), 2) late = records.filtered(lambda a: a.student_id == self.student1) self.assertEqual(late.state, "late")