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',
|
||||
'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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
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_injury
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue