fix: resolve portal emergency contact field reference errors and ACL issues

- Fix KeyError: 'relationship' by updating all controller methods to use 'contact_type' field
- Fix AttributeError: 'phone' by changing template references to use 'mobile' field
- Remove references to non-existent fields (email, notes) from emergency contact forms
- Fix XML syntax errors in emergency contact templates
- Grant create/write permissions to portal treatment professionals on res.partner model
- Update patient notes button URL from /my/patient/notes to /my/injury/notes
- Fix emergency contact form validation and field assignments in controllers
- Ensure all field references match actual sports.patient.contact model schema

Resolves portal 500 errors when viewing player details and managing emergency contacts.
Therapists can now successfully add/edit emergency contacts without field reference errors.
This commit is contained in:
Denis Durepos 2025-07-23 18:45:35 -04:00
parent 90dc45245e
commit a8aa7e71c2
4 changed files with 34 additions and 77 deletions

View file

@ -183,7 +183,7 @@ class PlayerManagementPortal(CustomerPortal):
'patient': patient,
'return_url': return_url,
'page_name': 'add_contact',
'relationship_types': request.env['sports.patient.contact']._fields['relationship'].selection,
'relationship_types': request.env['sports.patient.contact']._fields['contact_type'].selection,
}
return request.render('bemade_sports_clinic.portal_add_contact', values)
@ -209,13 +209,13 @@ class PlayerManagementPortal(CustomerPortal):
return request.redirect(f'/my/player?player_id={patient_id}')
# Required fields
if not post.get('name') or not post.get('relationship'):
if not post.get('name') or not post.get('contact_type'):
values = {
'patient': patient,
'error': _("Name and relationship are required fields"),
'error': _("Name and contact type are required fields"),
'return_url': f'/my/player?player_id={patient_id}',
'page_name': 'add_contact',
'relationship_types': request.env['sports.patient.contact']._fields['relationship'].selection,
'relationship_types': request.env['sports.patient.contact']._fields['contact_type'].selection,
}
values.update(post)
return request.render('bemade_sports_clinic.portal_add_contact', values)
@ -224,18 +224,12 @@ class PlayerManagementPortal(CustomerPortal):
vals = {
'patient_id': int(patient_id),
'name': post.get('name'),
'relationship': post.get('relationship'),
'contact_type': post.get('contact_type'),
}
# Optional fields
if post.get('phone'):
vals['phone'] = post.get('phone')
if post.get('email'):
vals['email'] = post.get('email')
if post.get('notes'):
vals['notes'] = post.get('notes')
if post.get('mobile'):
vals['mobile'] = post.get('mobile')
# Create the contact
request.env['sports.patient.contact'].sudo().create(vals)
@ -269,7 +263,7 @@ class PlayerManagementPortal(CustomerPortal):
'contact': contact,
'return_url': return_url,
'page_name': 'edit_contact',
'relationship_types': request.env['sports.patient.contact']._fields['relationship'].selection,
'relationship_types': request.env['sports.patient.contact']._fields['contact_type'].selection,
}
return request.render('bemade_sports_clinic.portal_edit_contact', values)
@ -300,14 +294,14 @@ class PlayerManagementPortal(CustomerPortal):
return request.redirect(f'/my/player?player_id={patient.id}')
# Required fields
if not post.get('name') or not post.get('relationship'):
if not post.get('name') or not post.get('contact_type'):
values = {
'patient': patient,
'contact': contact,
'error': _("Name and relationship are required fields"),
'error': _("Name and contact type are required fields"),
'return_url': f'/my/player?player_id={patient.id}',
'page_name': 'edit_contact',
'relationship_types': request.env['sports.patient.contact']._fields['relationship'].selection,
'relationship_types': request.env['sports.patient.contact']._fields['contact_type'].selection,
}
values.update(post)
return request.render('bemade_sports_clinic.portal_edit_contact', values)
@ -315,24 +309,14 @@ class PlayerManagementPortal(CustomerPortal):
# Prepare values for contact update
vals = {
'name': post.get('name'),
'relationship': post.get('relationship'),
'contact_type': post.get('contact_type'),
}
# Optional fields
if post.get('phone'):
vals['phone'] = post.get('phone')
if post.get('mobile'):
vals['mobile'] = post.get('mobile')
else:
vals['phone'] = False
if post.get('email'):
vals['email'] = post.get('email')
else:
vals['email'] = False
if post.get('notes'):
vals['notes'] = post.get('notes')
else:
vals['notes'] = False
vals['mobile'] = False
# Update the contact
contact.sudo().write(vals)

View file

@ -36,6 +36,6 @@ access_mail_notification_portal_tp,Portal TP Access for Mail Notifications,mail.
access_ir_attachment_portal_tp,Portal TP Access for Attachments,base.model_ir_attachment,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_res_users_portal_tp,Portal TP Access for Users,base.model_res_users,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_res_partner_portal_tp,Portal TP Access for Partners,base.model_res_partner,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_res_partner_portal_tp,Portal TP Access for Partners,base.model_res_partner,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_mail_followers_portal_tp,Portal TP Access for Followers,mail.model_mail_followers,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_bus_bus_portal_tp,Portal TP Access for Bus Messages,bus.model_bus_bus,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
36 access_ir_attachment_portal_tp Portal TP Access for Attachments base.model_ir_attachment bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
37 access_res_users_portal_tp Portal TP Access for Users base.model_res_users bemade_sports_clinic.group_portal_treatment_professional 1 0 0 0
38 access_res_partner_portal_tp Portal TP Access for Partners base.model_res_partner bemade_sports_clinic.group_portal_treatment_professional 1 0 1 0 1 0
39 access_mail_followers_portal_tp Portal TP Access for Followers mail.model_mail_followers bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
40 access_bus_bus_portal_tp Portal TP Access for Bus Messages bus.model_bus_bus bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
41

View file

@ -168,11 +168,11 @@
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
<label for="relationship">Relationship <span class="text-danger">*</span></label>
<select name="relationship" id="relationship" class="form-control" required="required">
<label for="contact_type">Contact Type <span class="text-danger">*</span></label>
<select name="contact_type" id="contact_type" class="form-control" required="required">
<option value="">Select...</option>
<t t-foreach="relationship_types" t-as="option">
<option t-att-value="option[0]" t-att-selected="relationship == option[0]">
<option t-att-value="option[0]" t-att-selected="contact_type == option[0]">
<t t-esc="option[1]"/>
</option>
</t>
@ -198,15 +198,7 @@
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<div class="form-group">
<label for="notes">Notes</label>
<textarea name="notes" id="notes" class="form-control"
rows="3" t-esc="notes"/>
</div>
</div>
</div>
<!-- Notes field removed as it doesn't exist in the model -->
<div class="row mt-4">
<div class="col-6">
@ -252,11 +244,11 @@
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
<label for="relationship">Relationship <span class="text-danger">*</span></label>
<select name="relationship" id="relationship" class="form-control" required="required">
<label for="contact_type">Contact Type <span class="text-danger">*</span></label>
<select name="contact_type" id="contact_type" class="form-control" required="required">
<option value="">Select...</option>
<t t-foreach="relationship_types" t-as="option">
<option t-att-value="option[0]" t-att-selected="contact.relationship == option[0]">
<option t-att-value="option[0]" t-att-selected="contact.contact_type == option[0]">
<t t-esc="option[1]"/>
</option>
</t>
@ -268,29 +260,17 @@
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
<label for="phone">Phone</label>
<input type="tel" name="phone" id="phone" class="form-control"
t-att-value="contact.phone"/>
<label for="mobile">Mobile</label>
<input type="tel" name="mobile" id="mobile" class="form-control"
t-att-value="contact.mobile"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" id="email" class="form-control"
t-att-value="contact.email"/>
</div>
<!-- Email field removed as it doesn't exist in the model -->
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<div class="form-group">
<label for="notes">Notes</label>
<textarea name="notes" id="notes" class="form-control"
rows="3" t-esc="contact.notes"/>
</div>
</div>
</div>
<!-- Notes field removed as it doesn't exist in the model -->
<div class="row mt-4">
<div class="col-6">

View file

@ -308,7 +308,7 @@
<a t-if="is_treatment_prof" t-att-href="'/my/activity/create?model=sports.patient&amp;res_id=%s' % player.id" class="btn btn-info me-2">
<i class="fa fa-tasks"></i> Add Activity
</a>
<a t-if="is_treatment_prof" t-att-href="'/my/patient/notes?patient_id=%s' % player.id" class="btn btn-info">
<a t-if="is_treatment_prof" t-att-href="'/my/injury/notes?patient_id=%s' % player.id" class="btn btn-info">
<i class="fa fa-clipboard-list"></i> View Notes
<span class="badge badge-pill badge-primary ms-1" t-if="player.treatment_note_count">
<t t-esc="player.treatment_note_count"/>
@ -527,21 +527,14 @@
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<strong t-esc="contact.name"/>
<span class="badge badge-secondary" t-esc="dict(contact._fields['relationship'].selection).get(contact.relationship)"/>
<span class="badge badge-secondary" t-esc="dict(contact._fields['contact_type'].selection).get(contact.contact_type)"/>
</div>
<div class="card-body">
<div t-if="contact.phone" class="mb-2">
<i class="fa fa-phone text-primary"></i>
<a t-att-href="'tel:%s' % contact.phone" t-esc="contact.phone" class="ms-2"/>
</div>
<div t-if="contact.email" class="mb-2">
<i class="fa fa-envelope text-primary"></i>
<a t-att-href="'mailto:%s' % contact.email" t-esc="contact.email" class="ms-2"/>
</div>
<div t-if="contact.notes" class="mb-2">
<i class="fa fa-sticky-note text-info"></i>
<span t-esc="contact.notes" class="ms-2 text-muted"/>
<div t-if="contact.mobile" class="mb-2">
<i class="fa fa-mobile text-primary"></i>
<a t-att-href="'tel:%s' % contact.mobile" t-esc="contact.mobile" class="ms-2"/>
</div>
<div class="btn-group mt-2">
<a t-att-href="'/my/player/contact/edit?contact_id=%s' % contact.id" class="btn btn-sm btn-primary">
<i class="fa fa-edit"></i> Edit