Added filters to sports.patient portal view.
This commit is contained in:
parent
a48450849c
commit
37ac446c1e
2 changed files with 202 additions and 15 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue