diff --git a/bemade_sports_clinic/__manifest__.py b/bemade_sports_clinic/__manifest__.py
index c8ed6e3..39dd9f2 100644
--- a/bemade_sports_clinic/__manifest__.py
+++ b/bemade_sports_clinic/__manifest__.py
@@ -42,7 +42,7 @@
'security/sports_clinic_groups.xml',
'security/ir.model.access.csv',
'security/sports_clinic_rules.xml',
- 'views/res_partner_views.xml',
+ 'views/sports_team_views.xml',
'views/sports_clinic_menus.xml',
'views/patient_views.xml',
'views/sports_clinic_portal_views.xml',
diff --git a/bemade_sports_clinic/controllers/team_staff_portal.py b/bemade_sports_clinic/controllers/team_staff_portal.py
index 733de27..566fc0f 100644
--- a/bemade_sports_clinic/controllers/team_staff_portal.py
+++ b/bemade_sports_clinic/controllers/team_staff_portal.py
@@ -19,7 +19,6 @@ class TeamStaffPortal(CustomerPortal):
partner = http.request.env.user.partner_id
return [
('staff_partner_ids', 'in', partner.id),
- ('type', '=', 'team'),
]
@classmethod
diff --git a/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml b/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml
index 48acbc1..c65e93d 100644
--- a/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml
+++ b/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml
@@ -19,19 +19,39 @@
+1 (514) 343-6111
https://www.umontreal.ca
-
+
Carabins (Football masculin)
- team
https://carabins.umontreal.ca/football
+
+ Jean U. DeMontreal
+ jean.u.demontreal@carabins.umontreal.ca
+
+
+
+
+
+ head_coach
+
+
+ Stretch Carabinier
+ streth.carabinier@carabins.umontreal.ca
+
+
+
+
+
+ head_trainer
+
Jean
Carabin
2001-08-23
jean.carabin@edudiants.umontreal.ca
- match
+ no
+ no
Rémi
@@ -39,21 +59,32 @@
2002-05-17
remi.lafracture@edudiants.umontreal.ca
- practice
+ yes
+ yes
-
+
Carabins (Hockey féminin)
- team
https://carabins.umontreal.ca/football
+
+
+
+ head_coach
+
+
+
+
+ head_trainer
+
Jeanne
Carabine
2001-08-23
jeanne.carabine@edudiants.umontreal.ca
- match
+ no
+ no_contact
Rimée
@@ -61,7 +92,8 @@
2004-11-22
rimee.lafracture@edudiants.umontreal.ca
- practice
+ yes
+ yes
Université Concordia
@@ -72,9 +104,8 @@
H3G 1M8
https://www.concordia.ca
-
+
Stingers (Hockey masculin)
- team
https://stingers.ca/mhockey/
@@ -84,7 +115,8 @@
2003-11-02
jo_stinger@encs.concordia.ca
- match
+ yes
+ yes
Jimmy
@@ -92,12 +124,13 @@
2001-01-05
jim_bee@jmsb.concordia.ca
- match
+ yes
+ yes
2023-05-17 18:53:00
Fracture du fémur
- 2024-02-01
+ 2024-02-01
@@ -105,7 +138,7 @@
2022-03-17 19:13:00
Entorse cheville droite
- 2022-03-23
+ 2022-03-23
@@ -113,15 +146,36 @@
2023-10-15 19:55:00
Commotion cérébrale mineure
- 2023-11-01
+ 2023-11-01
-
-
- Coach
-
+
+
+
+ head_coach
+
+
+
+ trainer
+ trainer
+
+
+
+
+ coach
+ coach
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bemade_sports_clinic/models/__init__.py b/bemade_sports_clinic/models/__init__.py
index 06a6a51..150c09b 100644
--- a/bemade_sports_clinic/models/__init__.py
+++ b/bemade_sports_clinic/models/__init__.py
@@ -1,2 +1,3 @@
from . import patient
from . import res_partner
+from . import sports_team
diff --git a/bemade_sports_clinic/models/patient.py b/bemade_sports_clinic/models/patient.py
index 0949a2e..1eec551 100644
--- a/bemade_sports_clinic/models/patient.py
+++ b/bemade_sports_clinic/models/patient.py
@@ -1,6 +1,6 @@
from odoo import models, fields, _, api
from odoo.exceptions import ValidationError
-from datetime import date
+from datetime import date, datetime
from dateutil.relativedelta import relativedelta
@@ -9,46 +9,46 @@ class Patient(models.Model):
_description = "Patient at a sports medicine clinic."
_inherit = ['mail.thread', 'mail.activity.mixin']
- first_name = fields.Char(required=True)
- last_name = fields.Char(required=True)
+ first_name = fields.Char(required=True, tracking=True)
+ last_name = fields.Char(required=True, tracking=True)
name = fields.Char(compute="_compute_name")
date_of_birth = fields.Date(
- groups="bemade_sports_clinic.group_sports_clinic_treatment_professional")
+ 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")
+ groups="bemade_sports_clinic.group_sports_clinic_treatment_professional",
+ tracking=True)
phone = fields.Char(unaccent=False,
- groups="bemade_sports_clinic.group_sports_clinic_user")
- email = fields.Char(groups="bemade_sports_clinic.group_sports_clinic_user")
- contact_ids = fields.One2many(comodel_name='sports.patient.contact',
- inverse_name='patient_id', string='Patient Contacts',
- groups="bemade_sports_clinic.group_sports_clinic_user")
- team_ids = fields.Many2many(comodel_name='res.partner',
+ 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.Many2many(comodel_name='sports.patient.contact',
+ relation="patient_contact_rel",
+ column1="patient_id",
+ column2="contact_id",
+ string='Patient Contacts',
+ groups="bemade_sports_clinic.group_sports_clinic_user")
+ team_ids = fields.Many2many(comodel_name='sports.team',
relation='sports_team_patient_rel',
column1='patient_id',
column2='team_id',
- string='Teams',
- domain=[('type', '=', 'team')])
- player_status = fields.Selection([
- ('practice', 'Practice'),
- ('match', 'Match'),
- ])
+ string='Teams', )
+ match_status = fields.Selection([ # Selection for easy expansion later
+ ('yes', 'Yes'),
+ ('no', 'No'),
+ ], required=True, default='yes', tracking=True)
+ practice_status = fields.Selection([
+ ('yes', 'Yes'),
+ ('no_contact', 'Yes, no contact'),
+ ('no', 'No')], tracking=True, required=True, default='yes')
+
injury_ids = fields.One2many(comodel_name='sports.patient.injury',
inverse_name='patient_id',
string='Injuries', )
- predicted_return_date = fields.Date(compute='_compute_predicted_return_date', )
- is_injured = fields.Boolean(compute='_compute_is_injured')
injured_since = fields.Date(compute='_compute_is_injured')
-
- # authorized_users = fields.One2many(compute='_compute_authorized_users', store=True)
- #
- # @api.depends('injury_ids.treatment_professional_ids', 'team_ids.staff_team_ids',
- # 'message_follower_ids')
- # def _compute_authorized_users(self):
- # for rec in self:
- # auth_users = self.env['res.users'].search(['|', ('partner_id', 'in',
- # rec.injury_ids.mapped('treatment_professional_ids').ids),
- # '|', ('partner_id', 'in', rec.team_ids.mapped('staff_team_ids').ids),
- # ('partner_id', 'in', rec.message_follower_ids.ids)])
+ predicted_return_date = fields.Date()
+ is_injured = fields.Boolean(compute="_compute_is_injured")
@api.depends('date_of_birth')
def _compute_age(self):
@@ -58,35 +58,16 @@ class Patient(models.Model):
else:
rec.age = relativedelta(date.today(), rec.date_of_birth).years
- @api.depends('injury_ids.predicted_return_date')
- def _compute_predicted_return_date(self):
- for rec in self:
- ongoing_injuries = rec.injury_ids.filtered(
- lambda r: r.predicted_return_date > date.today()).sorted(
- 'predicted_return_date')
- if ongoing_injuries:
- rec.predicted_return_date = ongoing_injuries[-1]. \
- predicted_return_date
- else:
- rec.predicted_return_date = False
-
@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()
- @api.depends('injury_ids.is_resolved')
+ @api.depends('practice_status', 'match_status')
def _compute_is_injured(self):
- injured = self.filtered(
- lambda r: r.injury_ids.filtered(lambda i: not i.is_resolved))
- for rec in injured:
- rec.is_injured = True
- rec.injured_since = rec.injury_ids.sorted('injury_date_time')[
- -1].injury_date_time.date()
- for rec in self - injured:
- rec.is_injured = False
- rec.injured_since = False
+ for rec in self:
+ rec.is_injured = rec.practice_status != 'yes' or rec.match_status != 'yes'
class PatientContact(models.Model):
@@ -99,9 +80,13 @@ class PatientContact(models.Model):
('Mother', 'mother'),
('Father', 'father'),
('other', 'Other'),
- ])
- phone = fields.Char(unaccent=False)
- patient_id = fields.Many2one(comodel_name='sports.patient', string="Patient")
+ ], required=True)
+ phone = fields.Char(unaccent=False, required=True)
+ patient_id = fields.Many2many(comodel_name='sports.patient', string="Patient",
+ relation="patient_contact_rel",
+ column1="contact_id", column2="patient_id",
+ groups="bemade_sports_clinic.group_sports_clinic_user",
+ required=True)
class PatientInjury(models.Model):
@@ -111,10 +96,12 @@ class PatientInjury(models.Model):
patient_id = fields.Many2one(comodel_name='sports.patient',
string="Patient",
- readonly=True)
+ readonly=True,
+ 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')
+ injury_date_time = fields.Datetime(string='Date and Time of Injury', required=True,
+ default=datetime.now())
internal_notes = fields.Html(tracking=True)
treatment_professional_ids = fields.Many2many(comodel_name='res.partner',
relation='patient_injury_treatment_pro_rel',
@@ -123,9 +110,9 @@ class PatientInjury(models.Model):
string='Treatment Professionals',
domain=[
('is_treatment_professional', '=',
- True)], )
- predicted_return_date = fields.Date(tracking=True)
- is_resolved = fields.Boolean(compute="_compute_is_resolved")
+ True)], tracking=True)
+ predicted_resolution_date = fields.Date(tracking=True)
+ is_resolved = fields.Boolean(tracking=True, required=True, default=False)
def write(self, vals):
super().write(vals)
@@ -142,10 +129,3 @@ class PatientInjury(models.Model):
- rec.message_follower_ids.mapped('partner_id'))
rec.message_subscribe(to_subscribe.ids)
return res
-
- @api.depends('predicted_return_date')
- def _compute_is_resolved(self):
- to_compute = self.filtered(lambda r: r.predicted_return_date)
- for rec in to_compute:
- rec.is_resolved = rec.predicted_return_date < date.today()
- (self - to_compute).is_resolved = False
diff --git a/bemade_sports_clinic/models/res_partner.py b/bemade_sports_clinic/models/res_partner.py
index 239af87..1481b4a 100644
--- a/bemade_sports_clinic/models/res_partner.py
+++ b/bemade_sports_clinic/models/res_partner.py
@@ -5,45 +5,16 @@ from odoo.exceptions import UserError
class Partner(models.Model):
_inherit = 'res.partner'
- patient_ids = fields.Many2many(comodel_name='sports.patient',
- relation='sports_team_patient_rel',
- column1='team_id',
- column2='patient_id',
- string='Players')
- player_count = fields.Integer(compute="_compute_player_counts")
- injured_count = fields.Integer(compute="_compute_player_counts")
- type = fields.Selection(selection_add=[('team', 'Sports Team'), ])
- staff_partner_ids = fields.Many2many(comodel_name='res.partner',
- relation='sports_team_staff_partner_rel',
- column1='team_id',
- column2='staff_partner_id',
- string='Staff')
- staff_team_ids = fields.Many2many(comodel_name='res.partner',
- relation='sports_team_staff_partner_rel',
- column1='staff_partner_id',
- column2='team_id',
- string='Teams')
-
is_treatment_professional = fields.Boolean(
- compute="_compute_is_treatment_professional",)
-
- def write(self, vals):
- teams = self.filtered(lambda r: r.type == 'team')
- if teams and 'type' in vals and vals['type'] != 'team':
- if teams.mapped('staff_partner_ids') or teams.mapped('patient_ids'):
- raise UserError(_('Sports team is related to patients and/or staff. '
- 'Its type cannot be changed until these relations are '
- 'removed.'))
- return super().write(vals)
-
- def unlink(self):
- teams = self.filtered(lambda r: r.type == 'team')
- if teams:
- if teams.mapped('staff_partner_ids') or teams.mapped('patient_ids'):
- raise UserError(_('Sports team is related to patients and/or staff. '
- 'Its type cannot be deleted until these relations are '
- 'removed. You may try archiving it instead.'))
- return super().unlink()
+ compute="_compute_is_treatment_professional", )
+ owned_team_ids = fields.One2many(comodel_name='sports.team',
+ inverse_name='parent_id')
+ staff_ids = fields.One2many(comodel_name='sports.team.staff',
+ inverse_name='team_id')
+ team_staff_rel_ids = fields.One2many(comodel_name='sports.team.staff',
+ inverse_name='partner_id',
+ string='Teams Served',
+ help='The teams this person works for.')
@api.depends('user_ids.groups_id')
def _compute_is_treatment_professional(self):
@@ -54,9 +25,3 @@ class Partner(models.Model):
'bemade_sports_clinic.group_sports_clinic_treatment_professional')
for rec in user_partners:
rec.is_treatment_professional = group in rec.user_ids.groups_id
-
- @api.depends('patient_ids.is_injured')
- def _compute_player_counts(self):
- for rec in self:
- rec.player_count = len(rec.patient_ids)
- rec.injured_count = len(rec.patient_ids.filtered(lambda p: p.is_injured))
diff --git a/bemade_sports_clinic/models/sports_team.py b/bemade_sports_clinic/models/sports_team.py
new file mode 100644
index 0000000..a929006
--- /dev/null
+++ b/bemade_sports_clinic/models/sports_team.py
@@ -0,0 +1,76 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+
+
+class SportsTeam(models.Model):
+ _name = "sports.team"
+ _description = "Sports Team"
+
+ name = fields.Char()
+ patient_ids = fields.Many2many(comodel_name='sports.patient',
+ relation='sports_team_patient_rel',
+ column1='team_id',
+ column2='patient_id',
+ string='Players')
+ player_count = fields.Integer(compute="_compute_player_counts")
+ injured_count = fields.Integer(compute="_compute_player_counts")
+ healthy_count = fields.Integer(compute="_compute_player_counts")
+ parent_id = fields.Many2one(comodel_name='res.partner', string='Parent Organization',
+ ondelete='restrict')
+ staff_ids = fields.One2many(comodel_name='sports.team.staff',
+ inverse_name='team_id')
+ head_coach_id = fields.Many2one(comodel_name='res.partner',
+ compute='_compute_head_coach')
+ head_coach_name = fields.Char(related='head_coach_id.name')
+ head_trainer_id = fields.Many2one(comodel_name='res.partner',
+ compute='_compute_head_trainer')
+ head_trainer_name = fields.Char(related='head_trainer_id.name')
+ website = fields.Char()
+
+ @api.depends('patient_ids.is_injured')
+ def _compute_player_counts(self):
+ for rec in self:
+ rec.player_count = len(rec.patient_ids)
+ rec.injured_count = len(rec.patient_ids.filtered(lambda p: p.is_injured))
+ rec.healthy_count = rec.player_count - rec.injured_count
+
+ @api.depends('staff_ids.role')
+ def _compute_head_coach(self):
+ for rec in self:
+ staff = rec.staff_ids.filtered(lambda r: r.role == 'head_coach')
+ rec.head_coach_id = staff.partner_id if staff else False
+
+ @api.depends('staff_ids.role')
+ def _compute_head_trainer(self):
+ for rec in self:
+ staff = rec.staff_ids.filtered(lambda r: r.role == 'head_trainer')
+ rec.head_trainer_id = staff.partner_id if staff else False
+
+
+class TeamStaff(models.Model):
+ _name = "sports.team.staff"
+ _description = "Relationship between staff members and their teams."
+
+ 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)
+ role = fields.Selection(selection=[
+ ('head_coach', 'Head Coach'),
+ ('head_trainer', 'Head Trainer'),
+ ('coach', 'Coach'),
+ ('trainer', 'Trainer'),
+ ('other', 'Other')
+ ], required=True)
+
+ _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')
+ for team in teams:
+ if len(team.staff_ids.filtered(lambda r: r.role == 'head_coach')) > 1:
+ raise ValidationError(_("A team can have only one head coach."))
+ if len(team.staff_ids.filtered(lambda r: r.role == 'head_trainer')) > 1:
+ raise ValidationError(_("A team can have only one head trainer."))
diff --git a/bemade_sports_clinic/security/ir.model.access.csv b/bemade_sports_clinic/security/ir.model.access.csv
index 2b76d10..793f2d9 100644
--- a/bemade_sports_clinic/security/ir.model.access.csv
+++ b/bemade_sports_clinic/security/ir.model.access.csv
@@ -5,4 +5,9 @@ access_patient_admin,Admin Access for Patients,model_sports_patient,group_sports
access_patient_portal,Portal Access for Patients,model_sports_patient,base.group_portal,1,1,1,0
access_patient_contact_user,User Access for Patient Contacts,model_sports_patient_contact,group_sports_clinic_user,1,1,1,1
access_injury_treatment_pro,Treatment Professional Access for Injuries,model_sports_patient_injury,group_sports_clinic_treatment_professional,1,1,1,1
-access_injury_portal,Portal Access for Injuries,model_sports_patient_injury,base.group_portal,1,0,0,0
\ No newline at end of file
+access_injury_portal,Portal Access for Injuries,model_sports_patient_injury,base.group_portal,1,0,0,0
+access_team_user,User Access for Teams,model_sports_team,group_sports_clinic_user,1,1,1,0
+access_team_admin,Admin Access for Teams,model_sports_team,group_sports_clinic_admin,1,1,1,1
+access_team_portal,Portal Access for Teams,model_sports_team,base.group_portal,1,0,0,0
+access_team_staff_user,User Access for Team Staff,model_sports_team_staff,group_sports_clinic_user,1,1,1,1
+access_team_staff_portal,Portal Access for Team Staff,model_sports_team_staff,base.group_portal,1,0,0,0
diff --git a/bemade_sports_clinic/views/patient_views.xml b/bemade_sports_clinic/views/patient_views.xml
index b237c28..763cf25 100644
--- a/bemade_sports_clinic/views/patient_views.xml
+++ b/bemade_sports_clinic/views/patient_views.xml
@@ -26,6 +26,8 @@
+
+
@@ -50,16 +52,33 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -80,9 +99,14 @@
-
+
+
+
+
+
+
@@ -93,7 +117,7 @@
-
+
diff --git a/bemade_sports_clinic/views/res_partner_views.xml b/bemade_sports_clinic/views/res_partner_views.xml
deleted file mode 100644
index 58a8952..0000000
--- a/bemade_sports_clinic/views/res_partner_views.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
- teams.view.search
- res.partner
-
-
-
-
-
-
-
-
-
- res.partner.view.form.inherit
-
- res.partner
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bemade_sports_clinic/views/sports_clinic_menus.xml b/bemade_sports_clinic/views/sports_clinic_menus.xml
index 8162657..e369bdc 100644
--- a/bemade_sports_clinic/views/sports_clinic_menus.xml
+++ b/bemade_sports_clinic/views/sports_clinic_menus.xml
@@ -3,21 +3,38 @@
Sports Teams
- res.partner
- [('type', '=', 'team')]
- list,form
+ sports.team
+ kanban,tree,form
+
+
+ kanban
+
+
+
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
Organizations
res.partner
- list,kanban,form
+ tree,kanban,form
[('is_company','=', True)]
Patients
sports.patient
- list,kanban,form
+ tree,kanban,form