Iteration 5 for sports clinic management.
- Team staff phone field replaced by mobile field - Doctor added to team staff options - Trainer replaced by therapist in team staff nomenclature - N/A option and onchange behaviour for injury date - Patient contact info added on the form view (linked to partner) - Allergies field added to patient record - Notes added to team information section of patient record - Fixed next page button on portal (controller routes update)
This commit is contained in:
parent
baad827f86
commit
032cc46666
6 changed files with 148 additions and 37 deletions
|
|
@ -28,14 +28,14 @@ class TeamStaffPortal(CustomerPortal):
|
|||
('team_ids', 'in', team_ids),
|
||||
]
|
||||
|
||||
@http.route(route=['/my/teams'], type='http', auth='user', website=True)
|
||||
@http.route(route=['/my/teams', '/my/teams/page/<int:page>'], type='http', auth='user', website=True)
|
||||
def view_teams(self, page=0, **kw):
|
||||
""" Display the list of teams that a portal user has access to """
|
||||
Teams = http.request.env['sports.team']
|
||||
domain = self._prepare_teams_domain()
|
||||
teams_count = Teams.search_count(domain)
|
||||
pgr = pager(url='/my/teams', total=teams_count,
|
||||
page=page, step=10, scope=10)
|
||||
page=page, step=10, scope=5)
|
||||
teams = http.request.env['sports.team'].search(self._prepare_teams_domain(),
|
||||
offset=pgr['offset'],
|
||||
limit=teams_count)
|
||||
|
|
@ -47,7 +47,7 @@ class TeamStaffPortal(CustomerPortal):
|
|||
'page_name': 'my_teams',
|
||||
})
|
||||
|
||||
@http.route(route=['/my/team/<int:team_id>'], type='http', auth='user', website=True)
|
||||
@http.route(route=['/my/team/<int:team_id>', '/my/team/<int:team_id>/page/<int:page>'], type='http', auth='user', website=True)
|
||||
def view_team(self, team_id, page=0, **kw):
|
||||
""" Display the information for a team including its list of players """
|
||||
team = http.request.env['sports.team'].browse(team_id)
|
||||
|
|
@ -55,7 +55,7 @@ class TeamStaffPortal(CustomerPortal):
|
|||
raise UserError(_('This team could not be found.'))
|
||||
players_count = team.player_count
|
||||
pgr = pager(url=f'/my/team/{team_id}', total=players_count, page=page, step=10,
|
||||
scope=10)
|
||||
scope=5)
|
||||
players = http.request.env['sports.patient'].search([
|
||||
('team_ids', 'in', team_id),
|
||||
], offset=pgr['offset'], limit=players_count)
|
||||
|
|
@ -70,13 +70,13 @@ class TeamStaffPortal(CustomerPortal):
|
|||
}
|
||||
)
|
||||
|
||||
@http.route(route=['/my/players'], 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):
|
||||
""" Display the list of players that the portal user has access to """
|
||||
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=10)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
<field name="practice_status">yes</field>
|
||||
</record>
|
||||
<record id="injury_1" model="sports.patient.injury">
|
||||
<field name="injury_date_time">2023-05-17 18:53:00</field>
|
||||
<field name="injury_date">2023-05-17</field>
|
||||
<field name="diagnosis">Fracture du fémur</field>
|
||||
<field name="predicted_resolution_date">2024-02-01</field>
|
||||
<field name="patient_id" ref="patient_1"/>
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
eval="[Command.set([ref('base.user_admin')])]"/>
|
||||
</record>
|
||||
<record id="injury_2" model="sports.patient.injury">
|
||||
<field name="injury_date_time">2022-03-17 19:13:00</field>
|
||||
<field name="injury_date">2022-03-17</field>
|
||||
<field name="diagnosis">Entorse cheville droite</field>
|
||||
<field name="predicted_resolution_date">2022-03-23</field>
|
||||
<field name="patient_id" ref="patient_1"/>
|
||||
|
|
@ -146,7 +146,7 @@
|
|||
eval="[Command.set([ref('base.user_admin')])]"/>
|
||||
</record>
|
||||
<record id="injury_3" model="sports.patient.injury">
|
||||
<field name="injury_date_time">2023-10-15 19:55:00</field>
|
||||
<field name="injury_date">2023-10-15</field>
|
||||
<field name="diagnosis">Commotion cérébrale mineure</field>
|
||||
<field name="predicted_resolution_date">2023-11-01</field>
|
||||
<field name="patient_id" ref="patient_3"/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from odoo import models, fields, _, api, Command
|
||||
from odoo.exceptions import ValidationError
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
|
||||
|
||||
class Patient(models.Model):
|
||||
|
|
@ -10,20 +11,28 @@ class Patient(models.Model):
|
|||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'last_name, first_name'
|
||||
|
||||
# res.partner fields
|
||||
partner_id = fields.Many2one(comodel_name='res.partner', string='Contact', ondelete='restrict', compute_sudo=True)
|
||||
first_name = fields.Char(required=True, tracking=True)
|
||||
last_name = fields.Char(required=True, tracking=True)
|
||||
name = fields.Char(compute="_compute_name")
|
||||
name = fields.Char(related='partner_id.name', compute="_compute_name", compute_sudo=True)
|
||||
phone = fields.Char(related='partner_id.phone', readonly=False)
|
||||
mobile = fields.Char(related='partner_id.mobile', readonly=False)
|
||||
street = fields.Char(related='partner_id.street', readonly=False)
|
||||
street2 = fields.Char(related='partner_id.street2', readonly=False)
|
||||
city = fields.Char(related='partner_id.city', readonly=False)
|
||||
state_id = fields.Many2one(related='partner_id.state_id', readonly=False)
|
||||
zip = fields.Char(related='partner_id.zip', readonly=False)
|
||||
country_id = fields.Many2one(related='partner_id.country_id', readonly=False)
|
||||
email = fields.Char(related='partner_id.email', readonly=False)
|
||||
|
||||
# Patient fields
|
||||
date_of_birth = fields.Date(
|
||||
groups="bemade_sports_clinic.group_sports_clinic_treatment_professional",
|
||||
tracking=True)
|
||||
age = fields.Integer(compute='_compute_age',
|
||||
groups="bemade_sports_clinic.group_sports_clinic_treatment_professional",
|
||||
tracking=True)
|
||||
mobile = fields.Char(unaccent=False,
|
||||
groups="bemade_sports_clinic.group_sports_clinic_user",
|
||||
tracking=True)
|
||||
email = fields.Char(groups="bemade_sports_clinic.group_sports_clinic_user",
|
||||
tracking=True)
|
||||
contact_ids = fields.One2many(comodel_name='sports.patient.contact',
|
||||
inverse_name='patient_id',
|
||||
string='Patient Contacts',
|
||||
|
|
@ -56,6 +65,8 @@ class Patient(models.Model):
|
|||
compute='_compute_stage')
|
||||
last_consultation_date = fields.Date()
|
||||
active_injury_count = fields.Integer(compute='_compute_active_injury_count')
|
||||
allergies = fields.Text()
|
||||
team_info_notes = fields.Html(string="Notes")
|
||||
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
|
|
@ -67,6 +78,15 @@ class Patient(models.Model):
|
|||
res.update({'team_ids': team_ids})
|
||||
return res
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for row in vals_list:
|
||||
if 'partner_id' not in row:
|
||||
row['partner_id'] = self.env['res.partner'].create({
|
||||
'name': self._get_name_from_first_and_last(row['first_name'], row['last_name'])
|
||||
}).id
|
||||
return super().create(vals_list)
|
||||
|
||||
@api.constrains('match_status', 'practice_status')
|
||||
def constrain_match_and_practice_status(self):
|
||||
""" Avoid invalid combinations of match and practice status:
|
||||
|
|
@ -109,16 +129,19 @@ class Patient(models.Model):
|
|||
@api.depends('first_name', 'last_name')
|
||||
def _compute_name(self):
|
||||
for rec in self:
|
||||
rec.name = ((rec.first_name or "") + " " + (rec.last_name or
|
||||
"")).strip()
|
||||
rec.name = self._get_name_from_first_and_last(rec.first_name, rec.last_name)
|
||||
|
||||
@api.depends('practice_status', 'match_status', 'injury_ids.injury_date_time')
|
||||
@api.model
|
||||
def _get_name_from_first_and_last(self, first_name, last_name):
|
||||
return ((first_name or "") + " " + (last_name or "")).strip()
|
||||
|
||||
@api.depends('practice_status', 'match_status', 'injury_ids.injury_date')
|
||||
def _compute_is_injured(self):
|
||||
for rec in self:
|
||||
rec.is_injured = rec.practice_status != 'yes' or rec.match_status != 'yes'
|
||||
if rec.is_injured:
|
||||
unresolved_injuries = rec.injury_ids.filtered(lambda r: not r.stage == 'resolved')
|
||||
rec.injured_since = unresolved_injuries and unresolved_injuries[0].injury_date_time
|
||||
rec.injured_since = unresolved_injuries and unresolved_injuries[0].injury_date
|
||||
else:
|
||||
rec.injured_since = False
|
||||
|
||||
|
|
@ -142,6 +165,28 @@ class Patient(models.Model):
|
|||
'res_id': self.id,
|
||||
}
|
||||
|
||||
@api.onchange('mobile', 'country_id')
|
||||
def _onchange_mobile_validation(self):
|
||||
if self.mobile:
|
||||
self.mobile = self._phone_format(self.mobile, force_format="INTERNATIONAL")
|
||||
|
||||
@api.onchange('phone', 'country_id')
|
||||
def _onchange_phone_validation(self):
|
||||
if self.phone:
|
||||
self.phone = self._phone_format(self.phone, force_format="INTERNATIONAL")
|
||||
|
||||
def _phone_format(self, number, force_format='E164'):
|
||||
country = self.country_id or self.env.company.country_id
|
||||
if not country or not number:
|
||||
return number
|
||||
return phone_validation.phone_format(
|
||||
number,
|
||||
country.code if country else None,
|
||||
country.phone_code if country else None,
|
||||
force_format=force_format,
|
||||
raise_exception=False
|
||||
)
|
||||
|
||||
|
||||
class PatientContact(models.Model):
|
||||
_name = 'sports.patient.contact'
|
||||
|
|
@ -157,6 +202,23 @@ class PatientContact(models.Model):
|
|||
mobile = fields.Char(unaccent=False, required=True)
|
||||
patient_id = fields.Many2one(comodel_name='sports.patient', string='Patient')
|
||||
|
||||
@api.onchange('mobile')
|
||||
def _onchange_mobile_validation(self):
|
||||
if self.mobile:
|
||||
self.mobile = self._phone_format(self.mobile, force_format="INTERNATIONAL")
|
||||
|
||||
def _phone_format(self, number, force_format='E164'):
|
||||
country = self.patient_id.country_id or self.env.company.country_id
|
||||
if not country or not number:
|
||||
return number
|
||||
return phone_validation.phone_format(
|
||||
number,
|
||||
country.code if country else None,
|
||||
country.phone_code if country else None,
|
||||
force_format=force_format,
|
||||
raise_exception=False
|
||||
)
|
||||
|
||||
|
||||
class PatientInjury(models.Model):
|
||||
_name = 'sports.patient.injury'
|
||||
|
|
@ -170,15 +232,15 @@ class PatientInjury(models.Model):
|
|||
required=True)
|
||||
patient_name = fields.Char(related="patient_id.name")
|
||||
diagnosis = fields.Char(tracking=True)
|
||||
injury_date_time = fields.Datetime(string='Date and Time of Injury', required=True,
|
||||
default=datetime.now())
|
||||
injury_date = fields.Date(string='Date and Time of Injury',
|
||||
default=date.today())
|
||||
injury_date_na = fields.Boolean(string="N/A", default=False)
|
||||
internal_notes = fields.Html(tracking=True)
|
||||
external_notes = fields.Html(tracking=True)
|
||||
treatment_professional_ids = fields.Many2many(comodel_name='res.users',
|
||||
relation='patient_injury_treatment_pro_rel',
|
||||
column1='patient_injury_id',
|
||||
column2='treatment_pro_id',
|
||||
string='Treatment Professionals',
|
||||
column2='treatment_pro_id', string='Treatment Professionals',
|
||||
domain=[
|
||||
('is_treatment_professional', '=',
|
||||
True)], tracking=True)
|
||||
|
|
@ -187,6 +249,24 @@ class PatientInjury(models.Model):
|
|||
help="The date when the injury was actually resolved.")
|
||||
stage = fields.Selection(selection=[('active', 'Active'), ('resolved', 'Resolved')], compute='_compute_stage')
|
||||
|
||||
@api.constrains('injury_date_na', 'injury_date')
|
||||
def constrain_date_blank_only_if_na(self):
|
||||
for rec in self:
|
||||
if not rec.injury_date_na and not rec.injury_date:
|
||||
raise ValidationError(_("If injury date is not set, the N/A box must be checked."))
|
||||
|
||||
@api.onchange('injury_date_na')
|
||||
def _onchange_injury_date_na(self):
|
||||
for rec in self:
|
||||
if rec.injury_date_na:
|
||||
rec.injury_date = None
|
||||
|
||||
@api.onchange('injury_date')
|
||||
def _onchange_injury_date(self):
|
||||
for rec in self:
|
||||
if rec.injury_date:
|
||||
rec.injury_date_na = False
|
||||
|
||||
@api.depends('resolution_date')
|
||||
def _compute_stage(self):
|
||||
for rec in self:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
|
|
@ -14,6 +15,12 @@ class Partner(models.Model):
|
|||
string='Teams Served',
|
||||
help='The teams this person works for.')
|
||||
teams_served_ids = fields.One2many(comodel_name='sports.team', compute='_compute_teams_served')
|
||||
patient_ids = fields.One2many(comodel_name='sports.patient', inverse_name='partner_id')
|
||||
|
||||
def write(self, vals):
|
||||
if self.patient_ids and 'name' in vals:
|
||||
raise ValidationError(_("To change a patient's name, change it from the patient form."))
|
||||
return super().write(vals)
|
||||
|
||||
@api.depends('team_staff_rel_ids.team_id')
|
||||
def _compute_teams_served(self):
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class TeamStaff(models.Model):
|
|||
|
||||
_sql_constraints = [('team_staff_unique', 'unique(team_id, partner_id)',
|
||||
'Each partner can only be related to a given team once.')]
|
||||
|
||||
@api.constrains('role')
|
||||
def _constrain_role(self):
|
||||
teams = self.mapped('team_id')
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@
|
|||
<field name="age" colspan="3"/>
|
||||
<label for="last_consultation_date" colspan="1"/>
|
||||
<span colspan="2">
|
||||
<field name="last_consultation_date" widget="date" class="oe_inline"/>
|
||||
<field name="last_consultation_date" widget="date" class="oe_inline"/>
|
||||
<button type="object" name="action_consulted_today"
|
||||
class="fa fa-refresh" title="Set to Today's Date"/>
|
||||
</span>
|
||||
|
||||
<field name="allergies"/>
|
||||
</group>
|
||||
<group>
|
||||
<separator string="Team Information"/>
|
||||
|
|
@ -96,21 +96,39 @@
|
|||
<field name="practice_status"/>
|
||||
<field name="predicted_return_date"/>
|
||||
<field name="return_date"/>
|
||||
<field name="team_info_notes"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Injuries">
|
||||
<field name="injury_ids"/>
|
||||
</page>
|
||||
<page string="Contacts">
|
||||
<field name="contact_ids">
|
||||
<tree string="Patient's Contacts"
|
||||
multi_edit="1" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="contact_type"/>
|
||||
<group>
|
||||
<group string="Patient Phone & Email">
|
||||
<field name="phone" widget="phone"/>
|
||||
<field name="mobile" widget="phone"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="email" widget="email"/>
|
||||
</group>
|
||||
<group string="Patient Address">
|
||||
<field name="street"/>
|
||||
<field name="street2"/>
|
||||
<field name="city"/>
|
||||
<field name="state_id"/>
|
||||
<field name="country_id"/>
|
||||
<field name="zip"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Other Contacts">
|
||||
<field name="contact_ids">
|
||||
<tree string="Patient's Contacts"
|
||||
multi_edit="1" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="contact_type"/>
|
||||
<field name="mobile" widget="phone"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</group>
|
||||
|
|
@ -139,9 +157,14 @@
|
|||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<group col="2">
|
||||
<field name="patient_id" invisible="1"/>
|
||||
<field name="injury_date_time"/>
|
||||
<span colspan="2">
|
||||
<label for="injury_date"/>
|
||||
<field name="injury_date" widget="date" class="oe_inline"/>
|
||||
<field name="injury_date_na" class="oe_inline"/>
|
||||
<label for="injury_date_na"/>
|
||||
</span>
|
||||
<field name="diagnosis"/>
|
||||
<field name="predicted_resolution_date"/>
|
||||
<field name="resolution_date"/>
|
||||
|
|
@ -169,7 +192,7 @@
|
|||
<tree string="Patient Injury History" editable="bottom"
|
||||
multi_edit="True">
|
||||
<field name="patient_id" invisible="1"/>
|
||||
<field name="injury_date_time" widget="datetime"/>
|
||||
<field name="injury_date" widget="date"/>
|
||||
<field name="diagnosis"/>
|
||||
<field name="predicted_resolution_date" widget="date"/>
|
||||
<field name="resolution_date" widget="date"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue