from odoo import api, fields, models from odoo.exceptions import UserError STYLE_SELECTION = [ ("bharatanatyam", "Bharatanatyam"), ("kathak", "Kathak"), ("hiphop", "Hip-Hop"), ("contemporary", "Contemporary"), ("zumba", "Zumba"), ] LEVEL_SELECTION = [ ("beginner", "Beginner"), ("intermediate", "Intermediate"), ("advanced", "Advanced"), ] class DanceEnrollmentRequest(models.Model): _name = "dance.enrollment.request" _description = "Website Enrollment Request" _inherit = ["mail.thread"] _order = "create_date desc, id desc" _rec_name = "reference" reference = fields.Char(default="New", readonly=True, copy=False, index=True) student_name = fields.Char(string="Student Name", required=True, tracking=True) date_of_birth = fields.Date() gender = fields.Selection( [("male", "Male"), ("female", "Female"), ("other", "Other")] ) guardian_name = fields.Char(string="Parent / Guardian", required=True, tracking=True) guardian_email = fields.Char(string="Email", required=True, tracking=True) guardian_phone = fields.Char(string="Phone", required=True, tracking=True) dance_style = fields.Selection(STYLE_SELECTION, string="Dance Style", tracking=True) preferred_level = fields.Selection(LEVEL_SELECTION, string="Preferred Level") batch_id = fields.Many2one("dance.batch", string="Preferred Batch") message = fields.Text() state = fields.Selection( [ ("new", "New Request"), ("contacted", "Contacted"), ("approved", "Approved"), ("rejected", "Rejected"), ("converted", "Enrolled"), ], default="new", required=True, tracking=True, ) student_id = fields.Many2one( "res.partner", string="Student Record", readonly=True, copy=False ) enrollment_id = fields.Many2one( "dance.enrollment", string="Enrollment", readonly=True, copy=False ) portal_user_id = fields.Many2one( "res.users", string="Guardian Login", readonly=True, copy=False ) @api.model_create_multi def create(self, vals_list): for vals in vals_list: if vals.get("reference", "New") in ("New", False): vals["reference"] = ( self.env["ir.sequence"].next_by_code("dance.enrollment.request") or "New" ) return super().create(vals_list) # ── Workflow ────────────────────────────────────────────────────────────── def action_mark_contacted(self): self.filtered(lambda r: r.state == "new").write({"state": "contacted"}) def action_approve(self): self.filtered(lambda r: r.state in ("new", "contacted")).write( {"state": "approved"} ) def action_reject(self): self.filtered(lambda r: r.state not in ("converted",)).write( {"state": "rejected"} ) def action_reset_to_new(self): self.filtered(lambda r: r.state != "converted").write({"state": "new"}) def action_convert(self): for req in self: if req.state == "converted": continue if req.state != "approved": raise UserError( "Only approved requests can be converted into an enrollment." ) guardian = req._find_or_create_guardian() student = req._create_student(guardian) enrollment = self.env["dance.enrollment"] if req.batch_id: enrollment = enrollment.create( { "student_id": student.id, "batch_id": req.batch_id.id, "status": "active", } ) req.write( { "student_id": student.id, "enrollment_id": enrollment.id if enrollment else False, "state": "converted", } ) req._grant_guardian_portal(guardian) req.message_post( body="Enrollment request converted — student record %s created." % student.display_name ) return True def action_open_student(self): self.ensure_one() return { "type": "ir.actions.act_window", "res_model": "res.partner", "res_id": self.student_id.id, "view_mode": "form", } # ── Conversion helpers ──────────────────────────────────────────────────── def _find_or_create_guardian(self): self.ensure_one() Partner = self.env["res.partner"] guardian = Partner.browse() if self.guardian_email: guardian = Partner.search( [("email", "=ilike", self.guardian_email)], limit=1 ) if not guardian: guardian = Partner.create( { "name": self.guardian_name, "email": self.guardian_email, "phone": self.guardian_phone, "is_student": False, } ) return guardian def _create_student(self, guardian): self.ensure_one() return self.env["res.partner"].create( { "name": self.student_name, "is_student": True, "date_of_birth": self.date_of_birth, "gender": self.gender, "dance_style": self.dance_style, "level": self.preferred_level, "guardian_id": guardian.id, "guardian_ids": [(4, guardian.id)], "guardian_phone": self.guardian_phone, "student_status": "active", "join_date": fields.Date.context_today(self), } ) def _grant_guardian_portal(self, guardian): """Create a portal login for the guardian so they can reach /parent.""" self.ensure_one() if self.portal_user_id or not guardian.email: return Users = self.env["res.users"].sudo() existing = Users.with_context(active_test=False).search( [("login", "=", guardian.email)], limit=1 ) if existing: self.portal_user_id = existing return portal_group = self.env.ref("base.group_portal") user = Users.with_context(no_reset_password=True).create( { "name": guardian.name, "login": guardian.email, "email": guardian.email, "partner_id": guardian.id, "groups_id": [(6, 0, [portal_group.id])], } ) self.portal_user_id = user try: user.with_context(create_user=True).action_reset_password() except Exception: # Mail server may not be configured — login still works once a # password is set manually. Don't block the conversion. pass