bemade_sports_clinic: initial commit.
This commit is contained in:
parent
96b77f4035
commit
f0034ac447
5 changed files with 179 additions and 0 deletions
1
bemade_sports_clinic/__init__.py
Normal file
1
bemade_sports_clinic/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
46
bemade_sports_clinic/__manifest__.py
Normal file
46
bemade_sports_clinic/__manifest__.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) October 2023 Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||||
|
#
|
||||||
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||||
|
# or modified copies of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
{
|
||||||
|
'name': 'Sports Clinic Management',
|
||||||
|
'version': '16.0.1.0.0',
|
||||||
|
'summary': 'Manage the patients of a sports medicine clinic.',
|
||||||
|
'description': """
|
||||||
|
Adds the notion of sports teams, players (patients), coaches and treatment
|
||||||
|
professionals. The core purpose of this module is to keep track of the treatment
|
||||||
|
history of players and to make it appropriately accessible to the various
|
||||||
|
involved parties (medical practitioners, clinic staff, team staff, and patients).
|
||||||
|
|
||||||
|
Internal users get access to patient data as appropriate, i.e.
|
||||||
|
treatment professionals get full access to their own patients' data, clinic
|
||||||
|
staff users get access to basic patient data needed for contacting patients, etc.
|
||||||
|
|
||||||
|
External users (portal users) can be added to give coaches and other team
|
||||||
|
personnel access to limited player data such as estimated return-to-play dates.
|
||||||
|
""",
|
||||||
|
'category': 'Services/Medical',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'https://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': ['base', 'mail'],
|
||||||
|
'data': [],
|
||||||
|
'demo': [],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
'application': True,
|
||||||
|
}
|
||||||
2
bemade_sports_clinic/models/__init__.py
Normal file
2
bemade_sports_clinic/models/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
from . import patient
|
||||||
|
from . import sports_team
|
||||||
80
bemade_sports_clinic/models/patient.py
Normal file
80
bemade_sports_clinic/models/patient.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
from odoo import models, fields, _, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from datetime import date
|
||||||
|
from dateutil import relativedelta
|
||||||
|
|
||||||
|
|
||||||
|
class Patient(models.Model):
|
||||||
|
_name = 'sports.patient'
|
||||||
|
|
||||||
|
first_name = fields.Char(required=True)
|
||||||
|
last_name = fields.Char(required=True)
|
||||||
|
date_of_birth = fields.Date()
|
||||||
|
age = fields.Integer(compute='_compute_age')
|
||||||
|
phone = fields.Char(unaccent=False)
|
||||||
|
email = fields.Char()
|
||||||
|
contact_ids = fields.One2many(comodel_name='sports.patient.contact',
|
||||||
|
inverse_name='patient_id', string='Patient Contacts', )
|
||||||
|
team_ids = fields.Many2many(comodel_name='res.partner',
|
||||||
|
relation='sports_team_patient_rel',
|
||||||
|
column1='patient_id',
|
||||||
|
column2='team_id',
|
||||||
|
string='Teams',
|
||||||
|
domain=[('type', '=', 'team')])
|
||||||
|
player_status = fields.Selection([
|
||||||
|
('practice', 'Practice'),
|
||||||
|
('match', 'Match'),
|
||||||
|
])
|
||||||
|
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')
|
||||||
|
|
||||||
|
@api.depends('date_of_birth')
|
||||||
|
def _compute_age(self):
|
||||||
|
for rec in self:
|
||||||
|
if not rec.date_of_birth:
|
||||||
|
rec.age = False
|
||||||
|
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]
|
||||||
|
else:
|
||||||
|
rec.predicted_return_date = False
|
||||||
|
|
||||||
|
|
||||||
|
class PatientContact(models.Model):
|
||||||
|
_name = 'sports.patient.contact'
|
||||||
|
|
||||||
|
sequence = fields.Integer(required=True, default=0)
|
||||||
|
name = fields.Char(unaccent=False)
|
||||||
|
contact_type = fields.Selection(selection=[
|
||||||
|
('Mother', 'mother'),
|
||||||
|
('Father', 'father'),
|
||||||
|
('other', 'Other'),
|
||||||
|
])
|
||||||
|
phone = fields.Char(unaccent=False)
|
||||||
|
patient_id = fields.Many2one(comodel_name='sports.patient', string="Patient")
|
||||||
|
|
||||||
|
|
||||||
|
class PatientInjury(models.Model):
|
||||||
|
_name = 'sports.patient.injury'
|
||||||
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||||
|
patient_id = fields.Many2one(comodel_name='sports.patient', string="Patient")
|
||||||
|
diagnosis = fields.Char(tracking=True)
|
||||||
|
injury_date_time = fields.Datetime(string='Date and Time of Injury')
|
||||||
|
internal_notes = fields.Html(tracking=True)
|
||||||
|
treatment_professional_ids = fields.Many2many(comodel_name='res.partner',
|
||||||
|
relation='patient_injury_treatment_pro_rel',
|
||||||
|
column1='patient_injury_id',
|
||||||
|
column2='treatment_pro_id',
|
||||||
|
string='Treatment Professionals',
|
||||||
|
domain=[
|
||||||
|
('type', '=', 'treatment_pro')], )
|
||||||
|
predicted_return_date = fields.Date(tracking=True)
|
||||||
50
bemade_sports_clinic/models/res_partner.py
Normal file
50
bemade_sports_clinic/models/res_partner.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
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')
|
||||||
|
type = fields.Selection(selection_add=[('team', 'Sports Team'),
|
||||||
|
('treatment_pro', 'Treatment Professional'),])
|
||||||
|
staff_partner_ids = fields.Many2many(comodel_name='res.partner',
|
||||||
|
relation='sports_team_staff_partner_rel',
|
||||||
|
column1='team_id',
|
||||||
|
column2='staff_partner_id',
|
||||||
|
string='Staff')
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
# Override to validate changes to the 'type' field
|
||||||
|
treatment_pros = self.filtered(lambda r: r.type == 'treatment_pro')
|
||||||
|
if treatment_pros and 'type' in vals and vals['type'] != 'treatment_pro':
|
||||||
|
injuries = self.env['sports.patient.injury'].search_count([('treatment_professional_ids', 'in', treatment_pros.ids)])
|
||||||
|
if injuries:
|
||||||
|
raise UserError(_('Partner type cannot be changed since this treatment '
|
||||||
|
'professional appears on a patient injury record.'))
|
||||||
|
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):
|
||||||
|
treatment_pros = self.filtered(lambda r: r.type == 'treatment_pro')
|
||||||
|
if treatment_pros:
|
||||||
|
injuries = self.env['sports.patient.injury'].search_count([('treatment_professional_ids', 'in', treatment_pros.ids)])
|
||||||
|
if injuries:
|
||||||
|
raise UserError(_('Treatment professional cannot be deleted since they '
|
||||||
|
'appears on a patient injury record.'))
|
||||||
|
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()
|
||||||
Loading…
Reference in a new issue