feat(bemade_sports_clinic): add Team Role Mass Assign wizard, views, and ACLs
- Add wizard models: team_role_mass_assign_wizard.py (wizard + line) - Register in models/__init__.py - Add views/team_role_mass_assign_wizard_views.xml - Update res_users_views.xml to expose the wizard (button/action) - Define ACLs for wizard and lines in security/ir.model.access.csv - Update __manifest__.py to load new security and views
This commit is contained in:
parent
917e04d018
commit
15f96ec795
6 changed files with 203 additions and 2 deletions
|
|
@ -92,8 +92,9 @@ This module provides a complete sports medicine clinic management solution with
|
||||||
"views/portal_event_create_template.xml",
|
"views/portal_event_create_template.xml",
|
||||||
"views/treatment_note_views.xml",
|
"views/treatment_note_views.xml",
|
||||||
"views/res_partner_views.xml",
|
"views/res_partner_views.xml",
|
||||||
"views/res_users_views.xml",
|
|
||||||
"views/task_to_event_wizard_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"],
|
"demo": ["data/demo/sports_clinic_demo_data.xml"],
|
||||||
"installable": True,
|
"installable": True,
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@ from . import project_task_security_test
|
||||||
from . import project_project
|
from . import project_project
|
||||||
from . import sports_event
|
from . import sports_event
|
||||||
from . import base_partner_merge
|
from . import base_partner_merge
|
||||||
|
from . import team_role_mass_assign_wizard
|
||||||
|
|
|
||||||
131
bemade_sports_clinic/models/team_role_mass_assign_wizard.py
Normal file
131
bemade_sports_clinic/models/team_role_mass_assign_wizard.py
Normal file
|
|
@ -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
|
||||||
|
|
@ -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_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_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_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
|
||||||
|
|
|
||||||
|
|
|
@ -6,6 +6,20 @@
|
||||||
<field name="inherit_id" ref="base.view_users_form"/>
|
<field name="inherit_id" ref="base.view_users_form"/>
|
||||||
<field name="model">res.users</field>
|
<field name="model">res.users</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<!-- Smart button to open mass team role assignment wizard -->
|
||||||
|
<xpath expr="//div[@name='button_box']" position="inside">
|
||||||
|
<button
|
||||||
|
class="oe_stat_button"
|
||||||
|
type="action"
|
||||||
|
name="%(bemade_sports_clinic.action_team_role_mass_assign)d"
|
||||||
|
icon="fa-users"
|
||||||
|
groups="base.group_system,bemade_sports_clinic.group_sports_clinic_admin"
|
||||||
|
>
|
||||||
|
<div class="o_stat_info">
|
||||||
|
<span class="o_stat_text">Assign Team Roles</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
<!-- Add the existing page for team access -->
|
<!-- Add the existing page for team access -->
|
||||||
<page name="preferences" position="before">
|
<page name="preferences" position="before">
|
||||||
<page name="sports_team_access" string="Sports Team Access"
|
<page name="sports_team_access" string="Sports Team Access"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Wizard Form View -->
|
||||||
|
<record id="view_team_role_mass_assign_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">team.role.mass.assign.wizard.form</field>
|
||||||
|
<field name="model">team.role.mass.assign.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Assign Team Roles" create="false" edit="false" delete="false">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="bulk_role" placeholder="Set role for selected teams"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="line_ids"
|
||||||
|
context="{'tree_view_ref': 'bemade_sports_clinic.view_team_role_mass_assign_line_tree'}"
|
||||||
|
widget="one2many_list"
|
||||||
|
editable="bottom"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button string="Apply" type="object" name="action_apply" class="btn-primary"/>
|
||||||
|
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Dedicated Tree View for Lines -->
|
||||||
|
<record id="view_team_role_mass_assign_line_tree" model="ir.ui.view">
|
||||||
|
<field name="name">team.role.mass.assign.line.tree</field>
|
||||||
|
<field name="model">team.role.mass.assign.line</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list editable="bottom" string="Teams">
|
||||||
|
<field name="selected"/>
|
||||||
|
<field name="team_id"/>
|
||||||
|
<field name="role"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Window Action -->
|
||||||
|
<record id="action_team_role_mass_assign" model="ir.actions.act_window">
|
||||||
|
<field name="name">Assign Team Roles</field>
|
||||||
|
<field name="res_model">team.role.mass.assign.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
<field name="context">{"default_user_id": active_id}</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in a new issue