from odoo import api, fields, models from odoo.exceptions import ValidationError class DanceBatch(models.Model): _name = "dance.batch" _description = "Dance Batch" _order = "start_date desc, name" name = fields.Char(required=True) dance_style = fields.Selection( [ ("bharatanatyam", "Bharatanatyam"), ("kathak", "Kathak"), ("hiphop", "Hip-Hop"), ("contemporary", "Contemporary"), ("zumba", "Zumba"), ], string="Dance Style", required=True, ) level = fields.Selection( [ ("beginner", "Beginner"), ("intermediate", "Intermediate"), ("advanced", "Advanced"), ], required=True, ) instructor_id = fields.Many2one("hr.employee", string="Instructor", required=True) room = fields.Char() capacity = fields.Integer(default=20) active_enrollment_count = fields.Integer( string="Current Strength", compute="_compute_enrollment_counts", store=True, ) enrollment_count = fields.Integer(compute="_compute_enrollment_counts", store=True) available_seats = fields.Integer(compute="_compute_enrollment_counts", store=True) is_almost_full = fields.Boolean(compute="_compute_capacity_warning") capacity_warning = fields.Char(compute="_compute_capacity_warning") start_date = fields.Date(required=True) end_date = fields.Date() start_time = fields.Float(string="Start Time") end_time = fields.Float(string="End Time") schedule_days = fields.Char( string="Schedule Days", help="Example: Sat or Mon/Wed", ) status = fields.Selection( [ ("upcoming", "Upcoming"), ("active", "Active"), ("completed", "Completed"), ("cancelled", "Cancelled"), ], default="upcoming", required=True, ) enrollment_ids = fields.One2many( "dance.enrollment", "batch_id", string="Enrollments", ) @api.depends("enrollment_ids.status", "capacity") def _compute_enrollment_counts(self): for batch in self: active_enrollments = batch.enrollment_ids.filtered( lambda enrollment: enrollment.status == "active" ) batch.active_enrollment_count = len(active_enrollments) batch.enrollment_count = len(batch.enrollment_ids) batch.available_seats = max(batch.capacity - len(active_enrollments), 0) @api.depends("active_enrollment_count", "capacity", "available_seats") def _compute_capacity_warning(self): for batch in self: batch.is_almost_full = bool( batch.capacity and batch.active_enrollment_count < batch.capacity and batch.available_seats <= 2 ) if batch.is_almost_full: batch.capacity_warning = "Batch almost full (%s/%s)" % ( batch.active_enrollment_count, batch.capacity, ) elif batch.capacity and batch.active_enrollment_count >= batch.capacity: batch.capacity_warning = "Batch is full (%s/%s)" % ( batch.active_enrollment_count, batch.capacity, ) else: batch.capacity_warning = False @api.constrains("capacity") def _check_capacity(self): for batch in self: if batch.capacity < 0: raise ValidationError("Capacity cannot be negative.") if batch.capacity and batch.active_enrollment_count > batch.capacity: raise ValidationError("Capacity cannot be less than current strength.") @api.constrains("start_date", "end_date") def _check_dates(self): for batch in self: if batch.end_date and batch.start_date and batch.end_date < batch.start_date: raise ValidationError("End date cannot be before start date.") @api.constrains("start_time", "end_time") def _check_times(self): for batch in self: if batch.end_time and batch.start_time and batch.end_time <= batch.start_time: raise ValidationError("End time must be after start time.") def action_view_enrollments(self): self.ensure_one() return { "name": "Enrollments", "type": "ir.actions.act_window", "res_model": "dance.enrollment", "view_mode": "tree,form", "domain": [("batch_id", "=", self.id)], "context": {"default_batch_id": self.id}, }