diff --git a/bemade_sports_clinic/__manifest__.py b/bemade_sports_clinic/__manifest__.py index 52a5cd9..b07fa4c 100644 --- a/bemade_sports_clinic/__manifest__.py +++ b/bemade_sports_clinic/__manifest__.py @@ -92,8 +92,9 @@ This module provides a complete sports medicine clinic management solution with "views/portal_event_create_template.xml", "views/treatment_note_views.xml", "views/res_partner_views.xml", - "views/res_users_views.xml", "views/task_to_event_wizard_views.xml", + "views/team_role_mass_assign_wizard_views.xml", + "views/res_users_views.xml", ], "demo": ["data/demo/sports_clinic_demo_data.xml"], "installable": True, diff --git a/bemade_sports_clinic/models/__init__.py b/bemade_sports_clinic/models/__init__.py index e56bbc4..5365dad 100644 --- a/bemade_sports_clinic/models/__init__.py +++ b/bemade_sports_clinic/models/__init__.py @@ -12,3 +12,4 @@ from . import project_task_security_test from . import project_project from . import sports_event from . import base_partner_merge +from . import team_role_mass_assign_wizard diff --git a/bemade_sports_clinic/models/team_role_mass_assign_wizard.py b/bemade_sports_clinic/models/team_role_mass_assign_wizard.py new file mode 100644 index 0000000..c305dfe --- /dev/null +++ b/bemade_sports_clinic/models/team_role_mass_assign_wizard.py @@ -0,0 +1,131 @@ +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class TeamRoleMassAssignWizard(models.TransientModel): + _name = "team.role.mass.assign.wizard" + _description = "Mass assign team roles to a user across teams" + + user_id = fields.Many2one("res.users", required=True, readonly=True) + partner_id = fields.Many2one( + "res.partner", related="user_id.partner_id", readonly=True, store=False + ) + bulk_role = fields.Selection( + selection=[ + ("head_coach", "Head Coach"), + ("head_therapist", "Head Therapist"), + ("coach", "Coach"), + ("therapist", "Therapist"), + ("doctor", "Doctor"), + ("other", "Other"), + ], + string="Set role for selected", + help="Pick a role to apply to all selected teams (and as default for newly selected teams).", + ) + line_ids = fields.One2many( + "team.role.mass.assign.line", + "wizard_id", + string="Teams", + ) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + user = self.env["res.users"].browse(self.env.context.get("default_user_id")) + if not user or not user.exists(): + raise UserError(_("Wizard must be opened from a user.")) + partner = user.partner_id + Team = self.env["sports.team"].sudo() + Staff = self.env["sports.team.staff"].sudo() + lines = [] + # Prefetch existing staff roles for the partner + staff_by_team = { + s.team_id.id: s for s in Staff.search([("partner_id", "=", partner.id)]) + } + for team in Team.search([], order="name"): + staff = staff_by_team.get(team.id) + lines.append( + ( + 0, + 0, + { + "team_id": team.id, + "selected": bool(staff), + "role": staff.role if staff else False, + }, + ) + ) + res.update({ + "user_id": user.id, + "line_ids": lines, + }) + return res + + @api.onchange("bulk_role") + def _onchange_bulk_role(self): + if not self.bulk_role: + return + for line in self.line_ids: + if line.selected: + line.role = self.bulk_role + + def action_apply(self): + self.ensure_one() + if not self.user_id or not self.partner_id: + raise UserError(_("No target user provided.")) + Staff = self.env["sports.team.staff"].sudo() + partner = self.partner_id + # Apply selections + for line in self.line_ids: + if not line.selected: + continue + if not line.role: + # If not specified but bulk_role exists, use it + role = self.bulk_role + if not role: + raise UserError( + _("Please choose a role for team '%s' or set a bulk role.") % (line.team_id.display_name,) + ) + else: + role = line.role + # Upsert staff record + staff = Staff.search([ + ("team_id", "=", line.team_id.id), + ("partner_id", "=", partner.id), + ], limit=1) + if staff: + staff.write({"role": role}) + else: + Staff.create({ + "team_id": line.team_id.id, + "partner_id": partner.id, + "role": role, + }) + # Do not remove existing team assignments that were unselected - explicit non-destructive behavior + return {"type": "ir.actions.act_window_close"} + + +class TeamRoleMassAssignLine(models.TransientModel): + _name = "team.role.mass.assign.line" + _description = "Mass assign team roles - line" + + wizard_id = fields.Many2one("team.role.mass.assign.wizard", required=True, ondelete="cascade") + selected = fields.Boolean(string="Assign") + team_id = fields.Many2one("sports.team", required=True, ondelete="restrict") + role = fields.Selection( + selection=[ + ("head_coach", "Head Coach"), + ("head_therapist", "Head Therapist"), + ("coach", "Coach"), + ("therapist", "Therapist"), + ("doctor", "Doctor"), + ("other", "Other"), + ], + string="Role", + ) + + @api.onchange("selected") + def _onchange_selected(self): + # If user toggles selection on and no role set yet, use wizard bulk role as default + if self.selected and not self.role and self.wizard_id.bulk_role: + self.role = self.wizard_id.bulk_role diff --git a/bemade_sports_clinic/security/ir.model.access.csv b/bemade_sports_clinic/security/ir.model.access.csv index 4bd2250..239ef30 100644 --- a/bemade_sports_clinic/security/ir.model.access.csv +++ b/bemade_sports_clinic/security/ir.model.access.csv @@ -66,4 +66,7 @@ access_project_tags_portal_tp,Portal TP Access for Project Tags,project.model_pr access_project_milestone_portal_tp,Portal TP Access for Project Milestones,project.model_project_milestone,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0 access_snailmail_letter_portal_tp,Portal TP Access for Snailmail Letters,snailmail.model_snailmail_letter,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0 access_snailmail_letter_portal_coach,Portal Coach Access for Snailmail Letters,snailmail.model_snailmail_letter,bemade_sports_clinic.group_portal_team_coach,1,0,0,0 - +access_team_role_mass_assign_wizard_admin,Admin Access for Team Role Mass Assign Wizard,model_team_role_mass_assign_wizard,base.group_system,1,1,1,1 +access_team_role_mass_assign_wizard_admin2,Clinic Admin Access for Team Role Mass Assign Wizard,model_team_role_mass_assign_wizard,bemade_sports_clinic.group_sports_clinic_admin,1,1,1,1 +access_team_role_mass_assign_line_admin,Admin Access for Team Role Mass Assign Line,model_team_role_mass_assign_line,base.group_system,1,1,1,1 +access_team_role_mass_assign_line_admin2,Clinic Admin Access for Team Role Mass Assign Line,model_team_role_mass_assign_line,bemade_sports_clinic.group_sports_clinic_admin,1,1,1,1 diff --git a/bemade_sports_clinic/views/res_users_views.xml b/bemade_sports_clinic/views/res_users_views.xml index 02f9d45..4e10253 100644 --- a/bemade_sports_clinic/views/res_users_views.xml +++ b/bemade_sports_clinic/views/res_users_views.xml @@ -6,6 +6,20 @@ res.users + + + + + + + + team.role.mass.assign.wizard.form + team.role.mass.assign.wizard + +
+ + + + + + + + + + +
+
+
+
+
+ + + + team.role.mass.assign.line.tree + team.role.mass.assign.line + + + + + + + + + + + + Assign Team Roles + team.role.mass.assign.wizard + form + new + {"default_user_id": active_id} + +