from psycopg2 import IntegrityError from odoo.exceptions import ValidationError from odoo.tests.common import TransactionCase from odoo.tools import mute_logger class TestAcademicCalendar(TransactionCase): def test_setting_current_on_create_unsets_previous(self): Year = self.env["mc.academic.year"] year_a = Year.create({ "name": "2025-26", "date_start": "2025-06-01", "date_end": "2026-04-30", "is_current": True, }) self.assertTrue(year_a.is_current) year_b = Year.create({ "name": "2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", "is_current": True, }) self.assertFalse(year_a.is_current, "Creating a new current year must unset the old one.") self.assertTrue(year_b.is_current) def test_setting_current_on_write_unsets_previous(self): Year = self.env["mc.academic.year"] year_a = Year.create({ "name": "2025-26", "date_start": "2025-06-01", "date_end": "2026-04-30", "is_current": True, }) year_b = Year.create({ "name": "2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", "is_current": False, }) year_b.write({"is_current": True}) self.assertFalse(year_a.is_current) self.assertTrue(year_b.is_current) @mute_logger("odoo.sql_db") def test_two_current_years_violate_db_index_when_orm_bypassed(self): # The write()/create() override auto-toggles is_current through the ORM. # The partial unique index is the actual enforcement layer for anything # that writes around it (direct SQL, a future bug in the toggle logic). Year = self.env["mc.academic.year"] Year.create({ "name": "2025-26", "date_start": "2025-06-01", "date_end": "2026-04-30", "is_current": True, }) with self.assertRaises(IntegrityError): with self.cr.savepoint(): self.env.cr.execute( "INSERT INTO mc_academic_year " "(name, date_start, date_end, is_current, company_id, create_uid, write_uid, create_date, write_date) " "VALUES ('2026-27', '2026-06-01', '2027-04-30', true, %s, %s, %s, now(), now())", (self.env.company.id, self.env.uid, self.env.uid), ) def test_year_end_before_start_raises(self): with self.assertRaises(ValidationError): self.env["mc.academic.year"].create({ "name": "Bad Year", "date_start": "2026-06-01", "date_end": "2026-05-01", }) def test_term_end_before_start_raises(self): year = self.env["mc.academic.year"].create({ "name": "2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", }) with self.assertRaises(ValidationError): self.env["mc.academic.term"].create({ "name": "Term 1", "year_id": year.id, "date_start": "2026-09-30", "date_end": "2026-06-01", }) def test_term_must_fall_within_year(self): year = self.env["mc.academic.year"].create({ "name": "2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", }) with self.assertRaises(ValidationError): self.env["mc.academic.term"].create({ "name": "Term 1", "year_id": year.id, "date_start": "2026-05-01", # before the year starts "date_end": "2026-09-30", }) @mute_logger("odoo.sql_db") def test_duplicate_term_name_in_same_year_rejected(self): year = self.env["mc.academic.year"].create({ "name": "2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", }) self.env["mc.academic.term"].create({ "name": "Term 1", "year_id": year.id, "date_start": "2026-06-01", "date_end": "2026-09-30", }) with self.assertRaises(IntegrityError): with self.cr.savepoint(): self.env["mc.academic.term"].create({ "name": "Term 1", "year_id": year.id, "date_start": "2026-10-01", "date_end": "2026-12-31", })