bemade_sports_clinic: split notifcations for internal/external

This commit is contained in:
Marc Durepos 2024-02-21 15:00:01 -05:00
parent 0a2ec5fd8d
commit ae385ed1f7
7 changed files with 293 additions and 55 deletions

View file

@ -18,7 +18,7 @@
#
{
'name': 'Sports Clinic Management',
'version': '16.0.1.5.6',
'version': '16.0.1.5.7',
'summary': 'Manage the patients of a sports medicine clinic.',
'description': """
Adds the notion of sports teams, players (patients), coaches and treatment

View file

@ -1,23 +1,87 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record id="subtype_patient_update" model="mail.message.subtype">
<field name="name">Patient File Update</field>
<field name="res_model">sports.patient</field>
<record id="subtype_patient_injury_external_update" model="mail.message.subtype">
<field name="name">Patient File Update (External)</field>
<field name="res_model">sports.patient.injury</field>
<field name="default" eval="True"/>
<field name="description">Patient's file has been updated.</field>
<field name="internal" eval="False"/>
<field name="sequence" eval="4"/>
<field name="hidden" eval="False"/>
<field name="description"></field>
</record>
<record id="subtype_patient_injury_internal_update" model="mail.message.subtype">
<field name="name">Patient File Update (Internal)</field>
<field name="res_model">sports.patient.injury</field>
<field name="default" eval="True"/>
<field name="internal" eval="True"/>
<field name="sequence" eval="4"/>
<field name="hidden" eval="False"/>
</record>
<record id="subtype_patient_injury_update" model="mail.message.subtype">
<field name="name">Patient Injury Status Update</field>
<field name="res_model">sports.patient.injury</field>
<record id="mail_template_patient_injury_status_update" model="mail.template">
<field name="name">Patient Status Update</field>
<field name="model_id" ref="bemade_sports_clinic.model_sports_patient_injury"/>
<field name="subject">Patient Injury Status Update for {{ object.patient_name }}</field>
<field name="body_html" type="html">
<p>An update has been posted to the injury record for <t t-out="object.patient_name"/>'s injury<t t-if="object.diagnosis">with
the diagnosis of <strong t-out="object.diagnosis"/></t>. Click
<a t-attf-href="{{ user.company_id.website }}/my/player?player_id={{ object.patient_id.id }}">here</a> to view the injury details.
</p>
</field>
</record>
<record id="mail_template_patient_injury_new_internal_note" model="mail.template">
<field name="name">Patient Status Update</field>
<field name="model_id" ref="bemade_sports_clinic.model_sports_patient_injury"/>
<field name="subject">Internal Note Updated for Patient {{ object.patient_name }}</field>
<field name="body_html" type="html">
<p>A new internal note has been posted to the injury record for <t t-out="object.patient_name"/>'s injury<t t-if="object.diagnosis">with
the diagnosis of <strong t-out="object.diagnosis"/></t>.
</p>
<div>
<p><strong>New Note: </strong></p>
<div><t t-out="object.internal_notes"/></div>
</div>
</field>
</record>
<record id="subtype_patient_external_update" model="mail.message.subtype">
<field name="name">Patient File Update (External)</field>
<field name="res_model">sports.patient</field>
<field name="default" eval="True"/>
<field name="description">Patient's injury was updated.</field>
<field name="internal" eval="False"/>
<field name="sequence" eval="4"/>
<field name="hidden" eval="False"/>
<field name="description"></field>
</record>
<record id="subtype_patient_internal_update" model="mail.message.subtype">
<field name="name">Patient File Update (Internal)</field>
<field name="res_model">sports.patient</field>
<field name="default" eval="True"/>
<field name="internal" eval="True"/>
<field name="sequence" eval="4"/>
<field name="hidden" eval="False"/>
</record>
<record id="mail_template_patient_status_update" model="mail.template">
<field name="name">Patient Status Update</field>
<field name="model_id" ref="bemade_sports_clinic.model_sports_patient"/>
<field name="subject">Patient Status Update for {{ object.name}}</field>
<field name="body_html" type="html">
<p>An update has been posted to the record for <t t-out="object.name"/>'s. Click
<a t-attf-href="{{ user.company_id.website }}/my/players">here</a> to view the details.
</p>
</field>
</record>
<record id="mail_template_patient_new_internal_note" model="mail.template">
<field name="name">Patient Status Update</field>
<field name="model_id" ref="bemade_sports_clinic.model_sports_patient"/>
<field name="subject">Internal Note Updated for Patient {{ object.name}}</field>
<field name="body_html" type="html">
<p>A new internal note has been posted to the record for <t t-out="object.name"/>'s.
</p>
<div>
<p><strong>New Note: </strong></p>
<div><t t-out="object.note"/></div>
</div>
</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,22 @@
from odoo import api, SUPERUSER_ID, Command
def migrate(cr, version):
env = api.Environment(cr, SUPERUSER_ID, {})
patient_followers = env['mail.followers'].search([('res_model', '=',
'sports.patient')])
injury_followers = env['mail.followers'].search([('res_model', '=',
'sports.patient.injury')])
for f in patient_followers:
if f.partner_id.user_id.has_group('base.group_user'):
subtype = env.ref('bemade_sports_clinic.subtype_patient_internal_update').id
else:
subtype = env.ref('bemade_sports_clinic.subtype_patient_external_update').id
f.write({'subtype_ids': [Command.set([subtype])]})
for f in injury_followers:
if f.partner_id.user_id.has_group('base.group_user'):
subtype = env.ref('bemade_sports_clinic.subtype_patient_injury_internal_update').id
else:
subtype = env.ref('bemade_sports_clinic.subtype_patient_injury_external_update').id
f.write({'subtype_ids': [Command.set([subtype])]})

View file

@ -1,3 +1,4 @@
from . import mail_followers
from . import patient
from . import patient_injury
from . import patient_contact

View file

@ -0,0 +1,46 @@
from odoo import models, fields, api, _
class MailFollowers(models.Model):
_inherit = "mail.followers"
# This is a user quality of life modification, to avoid duplicated message subtype subscription for internal
# and external notifications (since both subtypes are default=True).
def write(self, vals):
super().write(vals)
subtypes_map = self.__subtypes_map()
if self.res_model in subtypes_map and 'subtype_ids' in vals:
internal_subtype, external_subtype = subtypes_map.get(self.res_model)
if external_subtype in self.subtype_ids and internal_subtype in self.subtype_ids:
self.subtype_ids = self.subtype_ids - external_subtype
@api.model_create_multi
def create(self, vals_list):
recs = super().create(vals_list)
subtypes_map = self.__subtypes_map()
to_fix = {
model:
recs.filtered(
lambda rec: rec.res_model == model
and subtypes[0] in rec.subtype_ids
and subtypes[1] in rec.subtype_ids
) for model, subtypes in subtypes_map.items()
}
for model, records in to_fix.items():
for rec in records:
rec.subtype_ids = rec.subtype_ids - subtypes_map[model][1]
return recs
def __subtypes_map(self):
xml_id_fmt_string = "bemade_sports_clinic.subtype_{0}_{1}_update"
return {
'sports.patient': (
self.env.ref(xml_id_fmt_string.format('patient', 'internal')),
self.env.ref(xml_id_fmt_string.format('patient', 'external')),
),
'sports.patient.injury': (
self.env.ref(xml_id_fmt_string.format('patient_injury', 'internal')),
self.env.ref(xml_id_fmt_string.format('patient_injury', 'external')),
),
}

View file

@ -3,6 +3,21 @@ from odoo.exceptions import ValidationError
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo.addons.phone_validation.tools import phone_validation
from typing import Set, Tuple
external_tracking_fields = {
'last_consultation_date',
'match_status',
'practice_status',
'predicted_return_date',
'return_date',
}
internal_tracking_fields = {
'team_info_notes',
'age',
'date_of_birth',
}
class Patient(models.Model):
@ -30,43 +45,68 @@ class Patient(models.Model):
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)
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='sports.team',
relation='sports_team_patient_rel',
column1='patient_id',
column2='team_id',
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', )
age = fields.Integer(
compute='_compute_age',
groups="bemade_sports_clinic.group_sports_clinic_treatment_professional"
)
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='sports.team',
relation='sports_team_patient_rel',
column1='patient_id',
column2='team_id',
string='Teams',
)
match_status = fields.Selection( # Selection rather than bool for easy expansion later
selection=[
('yes', 'Yes'),
('no', 'No'),
],
required=True,
default='yes',
tracking=True)
practice_status = fields.Selection(
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',
)
injured_since = fields.Date(compute='_compute_is_injured')
predicted_return_date = fields.Date(tracking=True)
return_date = fields.Date(tracking=True,
help="When the player was cleared by medical staff to "
"return to match play.")
return_date = fields.Date(
tracking=True,
help="When the player was cleared by medical staff to "
"return to match play."
)
is_injured = fields.Boolean(compute="_compute_is_injured")
stage = fields.Selection(
selection=[('no_play', 'Injured'), ('practice_ok', 'Practice OK'), ('healthy', 'Play OK')],
selection=[
('no_play', 'Injured'),
('practice_ok', 'Practice OK'),
('healthy', 'Play OK')
],
compute='_compute_stage')
last_consultation_date = fields.Date(tracking=True)
active_injury_count = fields.Integer(compute='_compute_active_injury_count')
allergies = fields.Text()
team_info_notes = fields.Html(string="Notes")
team_info_notes = fields.Html(
string="Notes",
tracking=True,
)
def default_get(self, fields_list):
res = super().default_get(fields_list)
@ -187,20 +227,40 @@ class Patient(models.Model):
raise_exception=False
)
@api.model
def __get_track_internal_external(self, params: Set[str]) -> Tuple[bool, bool]:
""" Based on the fields being changed, determine if the notification being sent is meant for external
or internal followers, or neither."""
external = bool(external_tracking_fields & params)
internal = external or bool(internal_tracking_fields & params)
return external, internal
def _track_subtype(self, init_values):
# List of fields that should result in team staff notification
external_values = [
"last_consultation_date",
"match_status",
"practice_status",
"predicted_return_date",
"return_date",
"external_notes",
]
if any([v in init_values for v in external_values]):
return self.env.ref("bemade_sports_clinic.subtype_patient_update")
external, internal = self.__get_track_internal_external({key for key in init_values.keys()})
if external:
return self.env.ref('bemade_sports_clinic.subtype_patient_external_update')
elif internal:
return self.env.ref('bemade_sports_clinic.subtype_patient_internal_update')
else:
return self.env.ref('mail.mt_note')
def _track_template(self, changes):
res = super()._track_template(changes)
external, internal = self.__get_track_internal_external({change for change in changes})
if external:
first_external_field = (external_tracking_fields & set(changes)).pop()
res[first_external_field] = (
self.env.ref('bemade_sports_clinic.mail_template_patient_status_update'), {
'auto_delete_message': False,
'email_layout_xmlid': 'mail.mail_notification_light',
}
)
if 'internal_notes' in changes:
res['team_info_notes'] = (
self.env.ref('bemade_sports_clinic.mail_template_patient_new_internal_note'), {
'auto_delete_message': False,
'email_layout_xmlid': 'mail.mail_notification_light',
}
)
return res

View file

@ -2,6 +2,20 @@ from odoo import models, fields, api, _
from datetime import datetime, date
import pytz
from odoo.exceptions import ValidationError
from typing import Set, Tuple
external_tracking_fields = {
'diagnosis',
'predicted_resolution_date',
'resolution_date',
'external_notes',
}
# Include only fields not already included in external_tracking_fields here
internal_tracking_fields = {
'internal_notes',
'parental_consent',
}
class PatientInjury(models.Model):
@ -112,9 +126,40 @@ class PatientInjury(models.Model):
'context': self._context,
}
@api.model
def __get_track_internal_external(self, params: Set[str]) -> Tuple[bool, bool]:
""" Based on the fields being changed, determine if the notification being sent is meant for external
or internal followers, or neither."""
external = bool(external_tracking_fields & params)
internal = external or bool(internal_tracking_fields & params)
return external, internal
def _track_subtype(self, init_values):
if 'treatment_professional_ids' in init_values \
and len(init_values) == 1:
return self.env.ref('mail.mt_note')
external, internal = self.__get_track_internal_external({key for key in init_values.keys()})
if external:
return self.env.ref('bemade_sports_clinic.subtype_patient_injury_external_update')
elif internal:
return self.env.ref('bemade_sports_clinic.subtype_patient_injury_internal_update')
else:
return self.env.ref('bemade_sports_clinic.subtype_patient_injury_update')
return self.env.ref('mail.mt_note')
def _track_template(self, changes):
res = super()._track_template(changes)
external, internal = self.__get_track_internal_external({change for change in changes})
if external:
first_external_field = (external_tracking_fields & set(changes)).pop()
res[first_external_field] = (
self.env.ref('bemade_sports_clinic.mail_template_patient_injury_status_update'), {
'auto_delete_message': False,
'email_layout_xmlid': 'mail.mail_notification_light',
}
)
if 'internal_notes' in changes:
res['internal_notes'] = (
self.env.ref('bemade_sports_clinic.mail_template_patient_injury_new_internal_note'), {
'auto_delete_message': False,
'email_layout_xmlid': 'mail.mail_notification_light',
}
)
return res