2026-09-28 11:03:02 +05:30

159 lines
5.4 KiB
Python

from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.exceptions import ValidationError
class ResPartner(models.Model):
_inherit = "res.partner"
is_student = fields.Boolean(string="Is Student", default=False, index=True)
student_code = fields.Char(string="Student ID", copy=False, readonly=True, index=True)
guardian_id = fields.Many2one(
"res.partner",
string="Primary Guardian",
domain="[('is_student', '=', False)]",
)
guardian_ids = fields.Many2many(
"res.partner",
"student_guardian_rel",
"student_id",
"guardian_id",
string="Guardians",
domain="[('is_student', '=', False)]",
)
guardian_phone = fields.Char(string="Guardian Phone")
sibling_ids = fields.Many2many(
"res.partner",
"student_sibling_rel",
"student_id",
"sibling_id",
string="Sibling Students",
domain="[('is_student', '=', True)]",
)
date_of_birth = fields.Date(string="Date of Birth")
age = fields.Integer(string="Age", compute="_compute_age", store=True)
gender = fields.Selection(
[
("male", "Male"),
("female", "Female"),
("other", "Other"),
],
string="Gender",
)
medical_notes = fields.Text(string="Medical Notes")
dance_style = fields.Selection(
[
("bharatanatyam", "Bharatanatyam"),
("kathak", "Kathak"),
("hiphop", "Hip-Hop"),
("contemporary", "Contemporary"),
("zumba", "Zumba"),
],
string="Dance Style",
)
level = fields.Selection(
[
("beginner", "Beginner"),
("intermediate", "Intermediate"),
("advanced", "Advanced"),
],
string="Level",
)
student_status = fields.Selection(
[
("trial", "Trial"),
("active", "Active"),
("hold", "On Hold"),
("dropped", "Dropped"),
("alumni", "Alumni"),
],
default="trial",
string="Student Status",
tracking=True,
)
join_date = fields.Date(string="Join Date")
_sql_constraints = [
(
"student_code_unique",
"unique(student_code)",
"Student ID must be unique.",
),
]
@api.depends("date_of_birth")
def _compute_age(self):
today = fields.Date.today()
for partner in self:
partner.age = (
relativedelta(today, partner.date_of_birth).years
if partner.date_of_birth
else 0
)
@api.onchange("guardian_id")
def _onchange_guardian_id(self):
for partner in self:
if partner.guardian_id:
partner.guardian_phone = partner.guardian_id.phone or partner.guardian_id.mobile
@api.constrains("sibling_ids")
def _check_sibling_ids(self):
for partner in self:
if partner in partner.sibling_ids:
raise ValidationError("A student cannot be linked as their own sibling.")
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get("is_student") and not vals.get("student_code"):
vals["student_code"] = self._get_next_student_code()
if vals.get("guardian_id") and not vals.get("guardian_phone"):
guardian = self.env["res.partner"].browse(vals["guardian_id"])
vals["guardian_phone"] = guardian.phone or guardian.mobile
partners = super().create(vals_list)
partners._sync_sibling_links()
return partners
def write(self, vals):
old_sibling_map = {}
if "sibling_ids" in vals and not self.env.context.get("skip_sibling_sync"):
old_sibling_map = {partner.id: set(partner.sibling_ids.ids) for partner in self}
if vals.get("guardian_id") and "guardian_phone" not in vals:
guardian = self.env["res.partner"].browse(vals["guardian_id"])
vals = dict(vals, guardian_phone=guardian.phone or guardian.mobile)
result = super().write(vals)
if "sibling_ids" in vals and not self.env.context.get("skip_sibling_sync"):
self._sync_sibling_links(old_sibling_map)
return result
def _sync_sibling_links(self, old_sibling_map=None):
old_sibling_map = old_sibling_map or {}
for partner in self:
current_siblings = partner.sibling_ids.filtered(lambda sibling: sibling != partner)
old_siblings = self.browse(old_sibling_map.get(partner.id, set()))
removed_siblings = old_siblings - current_siblings
if removed_siblings:
removed_siblings.with_context(skip_sibling_sync=True).write(
{"sibling_ids": [(3, partner.id)]}
)
for sibling in current_siblings:
if partner not in sibling.sibling_ids:
sibling.with_context(skip_sibling_sync=True).write(
{"sibling_ids": [(4, partner.id)]}
)
def _get_next_student_code(self):
sequence = self.env["ir.sequence"]
for _attempt in range(100):
student_code = sequence.next_by_code("dance.student")
if not self.search_count([("student_code", "=", student_code)]):
return student_code
raise ValidationError("Could not generate a unique Student ID.")