bemade_sports_clinic: split notifcations for internal/external
This commit is contained in:
parent
0a2ec5fd8d
commit
ae385ed1f7
7 changed files with 293 additions and 55 deletions
|
|
@ -18,7 +18,7 @@
|
||||||
#
|
#
|
||||||
{
|
{
|
||||||
'name': 'Sports Clinic Management',
|
'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.',
|
'summary': 'Manage the patients of a sports medicine clinic.',
|
||||||
'description': """
|
'description': """
|
||||||
Adds the notion of sports teams, players (patients), coaches and treatment
|
Adds the notion of sports teams, players (patients), coaches and treatment
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,87 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="subtype_patient_update" model="mail.message.subtype">
|
<record id="subtype_patient_injury_external_update" model="mail.message.subtype">
|
||||||
<field name="name">Patient File Update</field>
|
<field name="name">Patient File Update (External)</field>
|
||||||
<field name="res_model">sports.patient</field>
|
<field name="res_model">sports.patient.injury</field>
|
||||||
<field name="default" eval="True"/>
|
<field name="default" eval="True"/>
|
||||||
<field name="description">Patient's file has been updated.</field>
|
|
||||||
<field name="internal" eval="False"/>
|
<field name="internal" eval="False"/>
|
||||||
<field name="sequence" eval="4"/>
|
<field name="sequence" eval="4"/>
|
||||||
<field name="hidden" eval="False"/>
|
<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>
|
||||||
<record id="subtype_patient_injury_update" model="mail.message.subtype">
|
<record id="mail_template_patient_injury_status_update" model="mail.template">
|
||||||
<field name="name">Patient Injury Status Update</field>
|
<field name="name">Patient Status Update</field>
|
||||||
<field name="res_model">sports.patient.injury</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="default" eval="True"/>
|
||||||
<field name="description">Patient's injury was updated.</field>
|
|
||||||
<field name="internal" eval="False"/>
|
<field name="internal" eval="False"/>
|
||||||
<field name="sequence" eval="4"/>
|
<field name="sequence" eval="4"/>
|
||||||
<field name="hidden" eval="False"/>
|
<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>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
22
bemade_sports_clinic/migrations/16.0.1.5.7/post-migrate.py
Normal file
22
bemade_sports_clinic/migrations/16.0.1.5.7/post-migrate.py
Normal 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])]})
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from . import mail_followers
|
||||||
from . import patient
|
from . import patient
|
||||||
from . import patient_injury
|
from . import patient_injury
|
||||||
from . import patient_contact
|
from . import patient_contact
|
||||||
|
|
|
||||||
46
bemade_sports_clinic/models/mail_followers.py
Normal file
46
bemade_sports_clinic/models/mail_followers.py
Normal 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')),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,21 @@ from odoo.exceptions import ValidationError
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from odoo.addons.phone_validation.tools import phone_validation
|
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):
|
class Patient(models.Model):
|
||||||
|
|
@ -30,43 +45,68 @@ class Patient(models.Model):
|
||||||
date_of_birth = fields.Date(
|
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)
|
tracking=True)
|
||||||
age = fields.Integer(compute='_compute_age',
|
age = fields.Integer(
|
||||||
groups="bemade_sports_clinic.group_sports_clinic_treatment_professional",
|
compute='_compute_age',
|
||||||
tracking=True)
|
groups="bemade_sports_clinic.group_sports_clinic_treatment_professional"
|
||||||
contact_ids = fields.One2many(comodel_name='sports.patient.contact',
|
)
|
||||||
inverse_name='patient_id',
|
contact_ids = fields.One2many(
|
||||||
string='Patient Contacts',
|
comodel_name='sports.patient.contact',
|
||||||
groups="bemade_sports_clinic.group_sports_clinic_user")
|
inverse_name='patient_id',
|
||||||
team_ids = fields.Many2many(comodel_name='sports.team',
|
string='Patient Contacts',
|
||||||
relation='sports_team_patient_rel',
|
groups="bemade_sports_clinic.group_sports_clinic_user"
|
||||||
column1='patient_id',
|
)
|
||||||
column2='team_id',
|
team_ids = fields.Many2many(
|
||||||
string='Teams', )
|
comodel_name='sports.team',
|
||||||
match_status = fields.Selection([ # Selection for easy expansion later
|
relation='sports_team_patient_rel',
|
||||||
('yes', 'Yes'),
|
column1='patient_id',
|
||||||
('no', 'No'),
|
column2='team_id',
|
||||||
], required=True, default='yes', tracking=True)
|
string='Teams',
|
||||||
practice_status = fields.Selection([
|
)
|
||||||
('yes', 'Yes'),
|
match_status = fields.Selection( # Selection rather than bool for easy expansion later
|
||||||
('no_contact', 'Yes, no contact'),
|
selection=[
|
||||||
('no', 'No')], tracking=True, required=True, default='yes')
|
('yes', 'Yes'),
|
||||||
|
('no', 'No'),
|
||||||
injury_ids = fields.One2many(comodel_name='sports.patient.injury',
|
],
|
||||||
inverse_name='patient_id',
|
required=True,
|
||||||
string='Injuries', )
|
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')
|
injured_since = fields.Date(compute='_compute_is_injured')
|
||||||
predicted_return_date = fields.Date(tracking=True)
|
predicted_return_date = fields.Date(tracking=True)
|
||||||
return_date = fields.Date(tracking=True,
|
return_date = fields.Date(
|
||||||
help="When the player was cleared by medical staff to "
|
tracking=True,
|
||||||
"return to match play.")
|
help="When the player was cleared by medical staff to "
|
||||||
|
"return to match play."
|
||||||
|
)
|
||||||
is_injured = fields.Boolean(compute="_compute_is_injured")
|
is_injured = fields.Boolean(compute="_compute_is_injured")
|
||||||
stage = fields.Selection(
|
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')
|
compute='_compute_stage')
|
||||||
last_consultation_date = fields.Date(tracking=True)
|
last_consultation_date = fields.Date(tracking=True)
|
||||||
active_injury_count = fields.Integer(compute='_compute_active_injury_count')
|
active_injury_count = fields.Integer(compute='_compute_active_injury_count')
|
||||||
allergies = fields.Text()
|
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):
|
def default_get(self, fields_list):
|
||||||
res = super().default_get(fields_list)
|
res = super().default_get(fields_list)
|
||||||
|
|
@ -187,20 +227,40 @@ class Patient(models.Model):
|
||||||
raise_exception=False
|
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):
|
def _track_subtype(self, init_values):
|
||||||
# List of fields that should result in team staff notification
|
external, internal = self.__get_track_internal_external({key for key in init_values.keys()})
|
||||||
external_values = [
|
|
||||||
"last_consultation_date",
|
if external:
|
||||||
"match_status",
|
return self.env.ref('bemade_sports_clinic.subtype_patient_external_update')
|
||||||
"practice_status",
|
elif internal:
|
||||||
"predicted_return_date",
|
return self.env.ref('bemade_sports_clinic.subtype_patient_internal_update')
|
||||||
"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")
|
|
||||||
else:
|
else:
|
||||||
return self.env.ref('mail.mt_note')
|
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
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,20 @@ from odoo import models, fields, api, _
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
import pytz
|
import pytz
|
||||||
from odoo.exceptions import ValidationError
|
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):
|
class PatientInjury(models.Model):
|
||||||
|
|
@ -112,9 +126,40 @@ class PatientInjury(models.Model):
|
||||||
'context': self._context,
|
'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):
|
def _track_subtype(self, init_values):
|
||||||
if 'treatment_professional_ids' in init_values \
|
external, internal = self.__get_track_internal_external({key for key in init_values.keys()})
|
||||||
and len(init_values) == 1:
|
|
||||||
return self.env.ref('mail.mt_note')
|
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:
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue