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),
|
('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
|
@classmethod
|
||||||
def _prepare_activities_domain(cls):
|
def _prepare_activities_domain(cls):
|
||||||
# Use controller-level team-based filtering for consistent security
|
# 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)
|
@http.route(route=['/my/players', '/my/players/page/<int:page>'], type='http', auth='user', website=True)
|
||||||
def view_players(self, page=0, **kw):
|
def view_players(self, page=1, **kw):
|
||||||
""" Display the list of players that the portal user has access to """
|
"""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()
|
teams_domain = self._prepare_teams_domain()
|
||||||
players_domain = self._prepare_players_domain(teams_domain)
|
base_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)
|
# Additional filters
|
||||||
players = http.request.env['sports.patient'].search(players_domain,
|
domain = list(base_players_domain)
|
||||||
offset=pgr['offset'],
|
first_name = (kw.get('first_name') or '').strip()
|
||||||
limit=players_count)
|
last_name = (kw.get('last_name') or '').strip()
|
||||||
return http.request.render(template='bemade_sports_clinic.portal_my_players',
|
team_id = kw.get('team_id')
|
||||||
qcontext={
|
organization_id = kw.get('organization_id')
|
||||||
'players_count': players_count,
|
match_status = kw.get('match_status')
|
||||||
'players': players,
|
practice_status = kw.get('practice_status')
|
||||||
'pager': pgr,
|
|
||||||
'page_name': 'my_players',
|
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',
|
@http.route(route=['/my/player'], type='http',
|
||||||
auth='user', website=True)
|
auth='user', website=True)
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,12 @@
|
||||||
</t>
|
</t>
|
||||||
</tbody>
|
</tbody>
|
||||||
</t>
|
</t>
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<t t-if="pager">
|
||||||
|
<t t-call="portal.pager"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
<template id="portal_my_team_players">
|
<template id="portal_my_team_players">
|
||||||
|
|
@ -409,6 +415,87 @@
|
||||||
<template id="portal_my_players">
|
<template id="portal_my_players">
|
||||||
<t t-call="portal.portal_layout">
|
<t t-call="portal.portal_layout">
|
||||||
<h1>Your Players</h1>
|
<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">
|
<t t-call="portal.portal_table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue