from odoo import api, fields, models from odoo.exceptions import ValidationError class McAcademicYear(models.Model): _name = "mc.academic.year" _inherit = ["mail.thread"] _description = "Academic Year" _order = "date_start desc" _rec_name = "name" name = fields.Char( string="Name", required=True, tracking=True, help="School-facing label, e.g. 2026-27.", ) date_start = fields.Date(string="Start Date", required=True, tracking=True) date_end = fields.Date(string="End Date", required=True, tracking=True) is_current = fields.Boolean(string="Current Year", default=False, tracking=True) term_ids = fields.One2many("mc.academic.term", "year_id", string="Terms") company_id = fields.Many2one( "res.company", string="Company", required=True, default=lambda self: self.env.company, ) _name_company_uniq = models.Constraint( "unique(name, company_id)", "An academic year with this name already exists for this company.", ) def init(self): # Belt-and-suspenders on top of the create/write auto-toggle below: # a partial unique index guarantees at most one current year per # company even if a write bypasses the ORM (direct SQL, a future # bug in the toggle logic, concurrent transactions). self.env.cr.execute( "CREATE UNIQUE INDEX IF NOT EXISTS mc_academic_year_one_current_per_company " "ON mc_academic_year (company_id) WHERE is_current = true" ) @api.constrains("date_start", "date_end") def _check_dates(self): for year in self: if year.date_start and year.date_end and year.date_end <= year.date_start: raise ValidationError( "Academic year '%s' end date must be after its start date." % year.name ) def _unset_other_current_years(self): for year in self: others = self.search([ ("id", "!=", year.id), ("company_id", "=", year.company_id.id), ("is_current", "=", True), ]) if others: others.write({"is_current": False}) @api.model_create_multi def create(self, vals_list): # Unset the existing current year for each affected company BEFORE # inserting the new one, and flush immediately: create() issues a # direct SQL INSERT that does not wait for unrelated pending writes # in the ORM cache, so without the flush here the old row is still # True in the database when the new row is inserted, tripping the # partial unique index mid-transaction. for vals in vals_list: if vals.get("is_current"): company_id = vals.get("company_id", self.env.company.id) self.search([ ("company_id", "=", company_id), ("is_current", "=", True), ]).write({"is_current": False}) self.env.flush_all() return super().create(vals_list) def write(self, vals): if vals.get("is_current"): self._unset_other_current_years() return super().write(vals)