Added filters to sports.patient portal view.

This commit is contained in:
Denis Durepos 2025-08-15 20:54:37 -04:00
parent a48450849c
commit 37ac446c1e
2 changed files with 202 additions and 15 deletions

View file

@ -33,6 +33,22 @@ class TeamStaffPortal(CustomerPortal):
('team_ids', 'in', team_ids),
]
@classmethod
def _get_accessible_teams(cls):
"""Teams accessible to current portal user (staff on)."""
user = http.request.env.user
partner = user.partner_id
team_staff_rels = partner.team_staff_rel_ids
team_ids = team_staff_rels.mapped('team_id.id')
return http.request.env['sports.team'].browse(team_ids).sorted('name')
@classmethod
def _get_organizations(cls):
"""Organizations (parent partners) of accessible teams."""
teams = cls._get_accessible_teams()
organizations = teams.mapped('parent_id').filtered(lambda p: p)
return organizations.sorted('name')
@classmethod
def _prepare_activities_domain(cls):
# Use controller-level team-based filtering for consistent security
@ -125,22 +141,106 @@ class TeamStaffPortal(CustomerPortal):
)
@http.route(route=['/my/players', '/my/players/page/<int:page>'], type='http', auth='user', website=True)
def view_players(self, page=0, **kw):
""" Display the list of players that the portal user has access to """
def view_players(self, page=1, **kw):
"""Display the list of players the portal user has access to, with filters.
Filters supported (GET params):
- first_name (ilike)
- last_name (ilike)
- team_id (exact)
- organization_id (team parent partner)
- match_status (exact)
- practice_status (exact)
"""
Patients = http.request.env['sports.patient']
# Base domain by accessible teams
teams_domain = self._prepare_teams_domain()
players_domain = self._prepare_players_domain(teams_domain)
players_count = http.request.env['sports.patient'].search_count(players_domain)
pgr = pager(url='/my/players', total=players_count, page=page, step=10, scope=5)
players = http.request.env['sports.patient'].search(players_domain,
offset=pgr['offset'],
limit=players_count)
return http.request.render(template='bemade_sports_clinic.portal_my_players',
qcontext={
'players_count': players_count,
'players': players,
'pager': pgr,
'page_name': 'my_players',
})
base_players_domain = self._prepare_players_domain(teams_domain)
# Additional filters
domain = list(base_players_domain)
first_name = (kw.get('first_name') or '').strip()
last_name = (kw.get('last_name') or '').strip()
team_id = kw.get('team_id')
organization_id = kw.get('organization_id')
match_status = kw.get('match_status')
practice_status = kw.get('practice_status')
if first_name:
domain.append(('first_name', 'ilike', first_name))
if last_name:
domain.append(('last_name', 'ilike', last_name))
if team_id:
try:
domain.append(('team_ids', 'in', [int(team_id)]))
except Exception:
pass
if organization_id:
try:
org_id = int(organization_id)
# players whose any team has this parent organization
team_ids = http.request.env['sports.team'].search([('parent_id', '=', org_id)]).ids
domain.append(('team_ids', 'in', team_ids or [0]))
except Exception:
pass
if match_status:
domain.append(('match_status', '=', match_status))
if practice_status:
domain.append(('practice_status', '=', practice_status))
# Count and pagination
total = Patients.search_count(domain)
pgr = pager(
url='/my/players',
total=total,
page=page,
step=self._items_per_page,
url_args={
'first_name': first_name,
'last_name': last_name,
'team_id': team_id,
'organization_id': organization_id,
'match_status': match_status,
'practice_status': practice_status,
},
)
# Query with ordering: last name, first name ASC
players = Patients.search(
domain,
order='last_name asc, first_name asc',
limit=self._items_per_page,
offset=pgr['offset'],
)
# Filter options
teams = self._get_accessible_teams()
organizations = self._get_organizations()
match_status_selection = dict(Patients._fields['match_status'].selection)
practice_status_selection = dict(Patients._fields['practice_status'].selection)
return http.request.render(
template='bemade_sports_clinic.portal_my_players',
qcontext={
'players_count': total,
'players': players,
'pager': pgr,
'page_name': 'my_players',
# filters current values
'first_name': first_name,
'last_name': last_name,
'team_id': int(team_id) if team_id else None,
'organization_id': int(organization_id) if organization_id else None,
'match_status': match_status,
'practice_status': practice_status,
# options
'teams': teams,
'organizations': organizations,
'match_status_selection': match_status_selection,
'practice_status_selection': practice_status_selection,
},
)
@http.route(route=['/my/player'], type='http',
auth='user', website=True)

View file

@ -160,6 +160,12 @@
</t>
</tbody>
</t>
<!-- Pagination -->
<div class="mt-3">
<t t-if="pager">
<t t-call="portal.pager"/>
</t>
</div>
</t>
</template>
<template id="portal_my_team_players">
@ -409,6 +415,87 @@
<template id="portal_my_players">
<t t-call="portal.portal_layout">
<h1>Your Players</h1>
<!-- Filters -->
<div class="container-fluid mb-3">
<div class="row">
<div class="col-12 d-flex justify-content-end mb-2">
<a href="/my/players" class="btn btn-outline-secondary btn-sm">
<i class="fa fa-times"/> Clear Filters
</a>
</div>
</div>
<div class="row">
<div class="col-12">
<form method="get" class="o_portal_search_panel" action="/my/players">
<div class="row g-2 mb-2">
<!-- First Name -->
<div class="col-md-3">
<input type="text" name="first_name" class="form-control" placeholder="First name"
t-att-value="first_name or ''" onchange="this.form.submit();"/>
</div>
<!-- Last Name -->
<div class="col-md-3">
<input type="text" name="last_name" class="form-control" placeholder="Last name"
t-att-value="last_name or ''" onchange="this.form.submit();"/>
</div>
<!-- Organization -->
<div class="col-md-3">
<select name="organization_id" class="form-select" onchange="this.form.submit();">
<option value="">All Organizations</option>
<t t-foreach="organizations" t-as="org">
<option t-att-value="org.id" t-att-selected="'selected' if organization_id and organization_id == org.id else None">
<t t-esc="org.name"/>
</option>
</t>
</select>
</div>
<!-- Team -->
<div class="col-md-3">
<select name="team_id" class="form-select" onchange="this.form.submit();">
<option value="">All Teams</option>
<t t-foreach="teams" t-as="team">
<option t-att-value="team.id" t-att-selected="team_id == team.id">
<t t-esc="team.name"/>
</option>
</t>
</select>
</div>
</div>
<div class="row g-2">
<!-- Match Status -->
<div class="col-md-3">
<select name="match_status" class="form-select" onchange="this.form.submit();">
<option value="">All Match Status</option>
<t t-foreach="match_status_selection.items()" t-as="opt">
<option t-att-value="opt[0]" t-att-selected="'selected' if match_status and match_status == opt[0] else None">
<t t-esc="opt[1]"/>
</option>
</t>
</select>
</div>
<!-- Practice Status -->
<div class="col-md-3">
<select name="practice_status" class="form-select" onchange="this.form.submit();">
<option value="">All Practice Status</option>
<t t-foreach="practice_status_selection.items()" t-as="opt">
<option t-att-value="opt[0]" t-att-selected="'selected' if practice_status and practice_status == opt[0] else None">
<t t-esc="opt[1]"/>
</option>
</t>
</select>
</div>
</div>
</form>
</div>
</div>
<!-- Record Count -->
<div class="row mt-2">
<div class="col-12">
<span class="text-muted">Showing <strong t-esc="players_count or 0"/> players</span>
</div>
</div>
</div>
<t t-call="portal.portal_table">
<thead>
<tr>