Completes iteration 4 without translations.

This commit is contained in:
Marc Durepos 2023-11-06 22:26:30 -05:00
parent 44f391802d
commit d6c40a456e
8 changed files with 96 additions and 31 deletions

View file

@ -37,7 +37,7 @@
'author': 'Bemade Inc.',
'website': 'https://www.bemade.org',
'license': 'OPL-1',
'depends': ['base', 'mail', 'portal'],
'depends': ['portal', 'contacts'],
'data': [
'security/sports_clinic_groups.xml',
'security/ir.model.access.csv',

View file

@ -88,10 +88,12 @@ class TeamStaffPortal(CustomerPortal):
'page_name': 'my_players',
})
@http.route(route=['/my/players/<int:player_id>'], type='http', auth='user', website=True)
def view_player(self, player_id, **kw):
@http.route(route=['/my/players/<int:player_id>', '/my/players/<int:player_id>/<int:team_id>'], type='http',
auth='user', website=True)
def view_player(self, player_id, team_id=None,**kw):
""" Display the active injuries for a given player. """
player = http.request.env['sports.patient'].browse(player_id)
team = team_id and http.request.env['sports.team'].browse(player_id)
if not player:
raise UserError(_('This player could not be found.'))
injuries = player.injury_ids.filtered(lambda r: r.stage == 'active')
@ -100,6 +102,6 @@ class TeamStaffPortal(CustomerPortal):
qcontext={
'player': player,
'injuries': injuries,
'page_name': 'my_player_injuries',
'team': team
}
)
)

View file

@ -57,6 +57,18 @@ class Patient(models.Model):
last_consultation_date = fields.Date()
active_injury_count = fields.Integer(compute='_compute_active_injury_count')
@api.constrains('match_status', 'practice_status')
def constrain_match_and_practice_status(self):
""" Avoid invalid combinations of match and practice status:
- Yes (match), No (practice)
- Yes (match), No Contact (practice)
"""
# combinations of (match_status, practice_status) that are valid
valid_combinations = [('yes', 'yes'), ('no', 'yes'), ('no', 'no_contact'), ('no', 'no')]
for rec in self:
if (rec.match_status, rec.practice_status) not in valid_combinations:
raise ValidationError(_("Invalid combination of match and practice status."))
@api.depends('injury_ids.stage')
def _compute_active_injury_count(self):
for rec in self:
@ -64,13 +76,17 @@ class Patient(models.Model):
@api.depends('match_status', 'practice_status')
def _compute_stage(self):
stage_map = {
('yes', 'yes'): 'healthy',
('no', 'yes'): 'practice_ok',
('no', 'no_contact'): 'practice_ok',
('no', 'no'): 'no_play',
}
for rec in self:
if rec.match_status == 'yes' and rec.practice_status == 'yes':
rec.stage = 'healthy'
elif rec.match_status == 'no' and rec.practice_status == 'no_contact':
rec.stage = 'practice_ok'
else:
rec.stage = 'no_play'
if (rec.match_status, rec.practice_status) not in stage_map:
rec.stage = False # not a valid combination, will be caught by constraint if save is attempted
continue
rec.stage = stage_map[(rec.match_status, rec.practice_status)]
@api.depends('date_of_birth')
def _compute_age(self):
@ -91,9 +107,8 @@ class Patient(models.Model):
for rec in self:
rec.is_injured = rec.practice_status != 'yes' or rec.match_status != 'yes'
if rec.is_injured:
rec.injured_since = \
rec.injury_ids and rec.injury_ids.filtered(lambda r: not r.stage == 'resolved').sorted(
'injury_date_time')[0].injury_date_time
unresolved_injuries = rec.injury_ids.filtered(lambda r: not r.stage == 'resolved')
rec.injured_since = unresolved_injuries and unresolved_injuries[0].injury_date_time
else:
rec.injured_since = False
@ -160,14 +175,15 @@ class PatientInjury(models.Model):
predicted_resolution_date = fields.Date(tracking=True)
resolution_date = fields.Date(tracking=True,
help="The date when the injury was actually resolved.")
stage = fields.Selection(selection=[('active', 'Active'), ('resolved', 'Resolved')], tracking=True, required=True,
default='active', readonly=False)
stage = fields.Selection(selection=[('active', 'Active'), ('resolved', 'Resolved')], compute='_compute_stage')
@api.constrains('stage')
def constrain_stage_on_resolution_date(self):
@api.depends('resolution_date')
def _compute_stage(self):
for rec in self:
if rec.stage == 'resolved' and not rec.resolution_date:
raise ValidationError(_('Cannot set an injury as resolved without setting the resolution date first.'))
if rec.resolution_date and rec.resolution_date <= date.today():
rec.stage = 'resolved'
else:
rec.stage = 'active'
def write(self, vals):
super().write(vals)

View file

@ -1,4 +1,4 @@
from odoo import models, fields, api, _
from odoo import models, fields, api, _, Command
from odoo.exceptions import ValidationError
@ -56,7 +56,7 @@ class TeamStaff(models.Model):
sequence = fields.Integer()
team_id = fields.Many2one(comodel_name='sports.team', string='Team', required=True)
partner_id = fields.Many2one(comodel_name='res.partner', string='Staff Member',
required=True)
required=True, domain=[('is_company', '=', False)])
role = fields.Selection(selection=[
('head_coach', 'Head Coach'),
('head_trainer', 'Head Trainer'),
@ -66,8 +66,11 @@ class TeamStaff(models.Model):
], required=True)
phone = fields.Char(related='partner_id.phone', readonly=False)
name = fields.Char(related='partner_id.name', readonly=False)
parent_id = fields.Many2one(related='partner_id.parent_id', readonly=False, string="Organization")
parent_id = fields.Many2one(related='partner_id.parent_id', readonly=False, string="Organization",
domain=[('is_company', '=', True)])
email = fields.Char(related='parent_id.email', readonly=False)
user_ids = fields.One2many(related='partner_id.user_ids', readonly=True)
has_portal_access = fields.Boolean(compute='_compute_has_portal_access')
_sql_constraints = [('team_staff_unique', 'unique(team_id, partner_id)',
'Each partner can only be related to a given team once.')]
@ -84,4 +87,22 @@ class TeamStaff(models.Model):
@api.onchange('phone')
def _onchange_phone_validation(self):
if self.phone:
self.phone = self.partner_id._phone_format(self.phone, force_format='INTERNATIONAL')
self.phone = self.partner_id._phone_format(self.phone, force_format='INTERNATIONAL')
@api.depends('user_ids', 'user_ids.groups_id')
def _compute_has_portal_access(self):
for rec in self:
rec.has_portal_access = bool(rec.user_ids.filtered(lambda r: r.has_group('base.group_portal'))) or bool(
rec.user_ids.filtered(lambda r: r.has_group('base.group_user'))) or bool(rec.partner_id.signup_token)
def action_revoke_portal_access(self):
group_portal = self.env.ref('base.group_portal')
group_public = self.env.ref('base.group_public')
self.user_ids.write(
{'groups_id': [Command.unlink(group_portal.id), Command.link(group_public.id)], 'active': False})
# Remove the signup token, so it cannot be used
self.partner_id.sudo().signup_token = False
def action_grant_portal_access(self):
wiz = self.env['portal.wizard'].create({'partner_ids': [(4, self.partner_id.id)]})
return wiz._action_open_modal()

View file

@ -8,8 +8,7 @@
<field name="groups" eval="[(6, 0, [ref('base.group_portal')])]"/>
<field name="domain_force">
[('id', 'in',
user.partner_id.team_staff_rel_ids.mapped('team_id').mapped(
'patient_ids').ids)]
user.partner_id.team_staff_rel_ids.team_id.patient_ids.ids)]
</field>
</record>
</data>

View file

@ -63,7 +63,7 @@
<tbody>
<t t-foreach="players" t-as="player">
<t t-set="stage" t-value="player.stage"/>
<t t-set="url" t-value="'/my/players/' + str(player.id)"/>
<t t-set="url" t-value="'/my/players/' + str(player.id) + '/' + str(team.id)"/>
<tr t-attf-class="{{ ('text-danger' if stage == 'no_play'
else 'text-warning') if stage != 'healthy'
else '' }}">
@ -191,13 +191,26 @@
t-attf-href="/my/teams?{{ keep_query() }}">Teams</a>
<t t-else="">Teams</t>
</li>
<li t-if="team" class="breadcrumb-item active" t-esc="team.name"/>
<li t-if="page_name == 'my_teams' and team" class="breadcrumb-item active" t-esc="team.name"/>
<li t-if="page_name == 'my_players'"
t-attf-class="breadcrumb-item #{'active ' if not player else ''}">
<a t-if="player"
t-attf-href="/my/players?{{ keep_query() }}">Players</a>
<t t-else="">Players</t>
</li>
<li t-if="player and page_name == 'my_player'" class="breadcrumb-item active">
<a t-attf-href="/my/players?{{ keep_query() }}">Players</a>
<span t-field="player.name"/>
</li>
<li t-if="player and team" class="breadcrumb-item">
<a href="/my/teams">Teams</a>
</li>
<li t-if="player" class="breadcrumb-item">
<a t-if="team" t-attf-href="/my/team/{{ team.id }}?{{ keep_query() }}">
<span t-field="team.name"/>
</a>
<a t-else="" t-attf-href="/my/players/{{ keep_query() }}">Players</a>
</li>
<li t-if="player" class="breadcrumb-item active">
<span t-field="player.name"/>
</li>

View file

@ -68,7 +68,7 @@
<field name="arch" type="xml">
<form>
<header>
<field name="stage" widget="statusbar" options="{'clickable': '1'}" />
<field name="stage" widget="statusbar" />
</header>
<sheet>
<div class="oe_button_box"></div>
@ -129,7 +129,7 @@
<field name="arch" type="xml">
<form>
<header>
<field name="stage" widget="statusbar" options="{'clickable': '1'}"/>
<field name="stage" widget="statusbar"/>
</header>
<sheet>
<div class="oe_title">

View file

@ -107,6 +107,20 @@
<field name="name"/>
<field name="role"/>
<field name="phone" widget="phone"/>
<field name="has_portal_access" invisible="1"/>
<field name="partner_id" invisible="1"/>
<button name="action_grant_portal_access"
type="object" icon="fa-user-plus"
class="oe_highlight"
attrs="{'invisible': [('has_portal_access', '=', True)]}"
title="Grant Portal Access"
groups="bemade_sports_clinic.group_sports_clinic_admin,base.group_system"/>
<button name="action_revoke_portal_access" type="object" icon="fa-ban"
class="oe_highlight"
attrs="{'invisible': [('has_portal_access', '=', False)]}"
title="Revoke Portal Access"
groups="bemade_sports_clinic.group_sports_clinic_admin,base.group_system"/>
</tree>
</field>
</group>
@ -126,7 +140,7 @@
<sheet>
<group>
<group>
<field name="name"/>
<field name="partner_id"/>
<field name="parent_id"/>
<field name="role"/>
</group>