Overhaul the "Personal" tab of profile view

should be much more useful now.. er, at least for those who track
contact info on the Person record, but not those who track on the
Customer record..
This commit is contained in:
Lance Edgar 2021-10-04 21:21:34 -04:00
parent 48864ab611
commit 6386b34516
2 changed files with 1336 additions and 145 deletions

File diff suppressed because it is too large Load diff

View file

@ -27,14 +27,16 @@ Person Views
from __future__ import unicode_literals, absolute_import
import datetime
import logging
import six
import sqlalchemy as sa
from sqlalchemy import orm
from rattail.db import model, api
from rattail.db.util import maxlen
from rattail.time import localtime
from rattail.util import OrderedDict
from rattail.util import OrderedDict, simple_error
import colander
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
@ -44,6 +46,9 @@ from tailbone import forms, grids
from tailbone.views import MasterView
log = logging.getLogger(__name__)
class PersonView(MasterView):
"""
Master view for the Person class.
@ -430,6 +435,9 @@ class PersonView(MasterView):
'instance_title': self.get_instance_title(person),
'today': localtime(self.rattail_config).date(),
'person_data': self.get_context_person(person),
'phone_type_options': self.get_phone_type_options(),
'email_type_options': self.get_email_type_options(),
'max_lengths': self.get_max_lengths(),
'customers_data': self.get_context_customers(person),
'members_data': self.get_context_members(person),
'employee': employee,
@ -442,17 +450,65 @@ class PersonView(MasterView):
template = 'view_profile_buefy' if use_buefy else 'view_profile'
return self.render_to_response(template, context)
def get_context_person(self, person):
def get_max_lengths(self):
model = self.model
return {
'address_street': maxlen(model.PersonMailingAddress.street),
'address_street2': maxlen(model.PersonMailingAddress.street2),
'address_city': maxlen(model.PersonMailingAddress.city),
'address_state': maxlen(model.PersonMailingAddress.state),
'address_zipcode': maxlen(model.PersonMailingAddress.zipcode),
}
def get_phone_type_options(self):
"""
Returns a list of "phone type" options, for use in dropdown.
"""
# TODO: should probably define this list somewhere else
phone_types = [
"Home",
"Mobile",
"Work",
"Other",
"Fax",
]
return [{'value': typ, 'label': typ}
for typ in phone_types]
def get_email_type_options(self):
"""
Returns a list of "email type" options, for use in dropdown.
"""
# TODO: should probably define this list somewhere else
email_types = [
"Home",
"Work",
"Other",
]
return [{'value': typ, 'label': typ}
for typ in email_types]
def get_context_person(self, person):
context = {
'uuid': person.uuid,
'first_name': person.first_name,
'middle_name': person.middle_name,
'last_name': person.last_name,
'display_name': person.display_name,
'view_url': self.get_action_url('view', person),
'view_profile_url': self.get_action_url('view_profile', person),
'phones': self.get_context_phones(person),
'emails': self.get_context_emails(person),
}
if person.address:
context['address'] = self.get_context_address(person.address)
return context
def get_context_address(self, address):
person = address.person
return {
'uuid': address.uuid,
'street': address.street,
@ -461,6 +517,7 @@ class PersonView(MasterView):
'state': address.state,
'zipcode': address.zipcode,
'display': six.text_type(address),
'invalid': self.handler.address_is_invalid(person, address),
}
def get_context_customers(self, person):
@ -547,6 +604,270 @@ class PersonView(MasterView):
customer = handler.ensure_customer(person)
return customer
def profile_edit_name(self):
"""
View which allows a person's name to be updated.
"""
person = self.get_instance()
data = dict(self.request.json_body)
self.handler.update_names(person,
first=data['first_name'],
middle=data['middle_name'],
last=data['last_name'])
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def get_context_phones(self, person):
data = []
for phone in person.phones:
data.append({
'uuid': phone.uuid,
'type': phone.type,
'number': phone.number,
'preferred': phone.preferred,
'preference': phone.preference,
})
return data
def profile_add_phone(self):
"""
View which adds a new phone number for the person.
"""
person = self.get_instance()
data = dict(self.request.json_body)
try:
phone = self.handler.add_phone(person, data['phone_number'],
type=data['phone_type'],
preferred=data['phone_preferred'])
except Exception as error:
log.warning("failed to add phone", exc_info=True)
return {'error': simple_error(error)}
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_update_phone(self):
"""
View which updates a phone number for the person.
"""
person = self.get_instance()
data = dict(self.request.json_body)
phone = self.Session.query(model.PersonPhoneNumber).get(data['phone_uuid'])
if not phone:
return {'error': "Phone not found."}
kwargs = {
'number': data['phone_number'],
'type': data['phone_type'],
}
if 'phone_preferred' in data:
kwargs['preferred'] = data['phone_preferred']
try:
phone = self.handler.update_phone(person, phone, **kwargs)
except Exception as error:
log.warning("failed to update phone", exc_info=True)
return {'error': simple_error(error)}
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_delete_phone(self):
"""
View which allows a person's phone number to be deleted.
"""
person = self.get_instance()
data = dict(self.request.json_body)
# validate phone
phone = self.Session.query(model.PersonPhoneNumber).get(data['phone_uuid'])
if not phone:
return {'error': "Phone not found."}
if phone not in person.phones:
return {'error': "Phone does not belong to this person."}
# remove phone
person.remove_phone(phone)
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_set_preferred_phone(self):
"""
View which allows a person's "preferred" phone to be set.
"""
person = self.get_instance()
data = dict(self.request.json_body)
# validate phone
phone = self.Session.query(model.PersonPhoneNumber).get(data['phone_uuid'])
if not phone:
return {'error': "Phone not found."}
if phone not in person.phones:
return {'error': "Phone does not belong to this person."}
# update phone preference
person.set_primary_phone(phone)
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def get_context_emails(self, person):
data = []
for email in person.emails:
data.append({
'uuid': email.uuid,
'type': email.type,
'address': email.address,
'invalid': email.invalid,
'preferred': email.preferred,
'preference': email.preference,
})
return data
def profile_add_email(self):
"""
View which adds a new email address for the person.
"""
person = self.get_instance()
data = dict(self.request.json_body)
kwargs = {
'type': data['email_type'],
'invalid': False,
}
if 'email_preferred' in data:
kwargs['preferred'] = data['email_preferred']
try:
email = self.handler.add_email(person, data['email_address'], **kwargs)
except Exception as error:
log.warning("failed to add email", exc_info=True)
return {'error': simple_error(error)}
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_update_email(self):
"""
View which updates an email address for the person.
"""
person = self.get_instance()
data = dict(self.request.json_body)
email = self.Session.query(model.PersonEmailAddress).get(data['email_uuid'])
if not email:
return {'error': "Email not found."}
try:
email = self.handler.update_email(person, email,
address=data['email_address'],
type=data['email_type'],
invalid=data['email_invalid'])
except Exception as error:
log.warning("failed to add email", exc_info=True)
return {'error': simple_error(error)}
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_delete_email(self):
"""
View which allows a person's email address to be deleted.
"""
person = self.get_instance()
data = dict(self.request.json_body)
# validate email
email = self.Session.query(model.PersonEmailAddress).get(data['email_uuid'])
if not email:
return {'error': "Email not found."}
if email not in person.emails:
return {'error': "Email does not belong to this person."}
# remove email
person.remove_email(email)
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_set_preferred_email(self):
"""
View which allows a person's "preferred" email to be set.
"""
person = self.get_instance()
data = dict(self.request.json_body)
# validate email
email = self.Session.query(model.PersonEmailAddress).get(data['email_uuid'])
if not email:
return {'error': "Email not found."}
if email not in person.emails:
return {'error': "Email does not belong to this person."}
# update email preference
person.set_primary_email(email)
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_edit_address(self):
"""
View which allows a person's mailing address to be updated.
"""
person = self.get_instance()
data = dict(self.request.json_body)
# update person address
address = person.address
if not address:
address = person.add_address()
address.street = data['street']
address.street2 = data['street2']
address.city = data['city']
address.state = data['state']
address.zipcode = data['zipcode']
self.handler.mark_address_invalid(person, address, data['invalid'])
self.Session.flush()
return {
'success': True,
'person': self.get_context_person(person),
}
def profile_start_employee(self):
"""
View which will cause the person to start being an employee.
@ -786,6 +1107,101 @@ class PersonView(MasterView):
config.add_view(cls, attr='view_profile', route_name='{}.view_profile'.format(route_prefix),
permission='{}.view_profile'.format(permission_prefix))
# profile - edit personal details
config.add_tailbone_permission('people_profile',
'people_profile.edit_person',
"Edit the Personal details")
# profile - edit name
config.add_route('{}.profile_edit_name'.format(route_prefix),
'{}/profile/edit-name'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_edit_name',
route_name='{}.profile_edit_name'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - add phone
config.add_route('{}.profile_add_phone'.format(route_prefix),
'{}/profile/add-phone'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_add_phone',
route_name='{}.profile_add_phone'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - update phone
config.add_route('{}.profile_update_phone'.format(route_prefix),
'{}/profile/update-phone'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_update_phone',
route_name='{}.profile_update_phone'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - delete phone
config.add_route('{}.profile_delete_phone'.format(route_prefix),
'{}/profile/delete-phone'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_delete_phone',
route_name='{}.profile_delete_phone'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - set preferred phone
config.add_route('{}.profile_set_preferred_phone'.format(route_prefix),
'{}/profile/set-preferred-phone'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_set_preferred_phone',
route_name='{}.profile_set_preferred_phone'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - add email
config.add_route('{}.profile_add_email'.format(route_prefix),
'{}/profile/add-email'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_add_email',
route_name='{}.profile_add_email'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - update email
config.add_route('{}.profile_update_email'.format(route_prefix),
'{}/profile/update-email'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_update_email',
route_name='{}.profile_update_email'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - delete email
config.add_route('{}.profile_delete_email'.format(route_prefix),
'{}/profile/delete-email'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_delete_email',
route_name='{}.profile_delete_email'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - set preferred email
config.add_route('{}.profile_set_preferred_email'.format(route_prefix),
'{}/profile/set-preferred-email'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_set_preferred_email',
route_name='{}.profile_set_preferred_email'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - edit address
config.add_route('{}.profile_edit_address'.format(route_prefix),
'{}/profile/edit-address'.format(instance_url_prefix),
request_method='POST')
config.add_view(cls, attr='profile_edit_address',
route_name='{}.profile_edit_address'.format(route_prefix),
renderer='json',
permission='people_profile.edit_person')
# profile - start employee
config.add_route('{}.profile_start_employee'.format(route_prefix), '{}/profile/start-employee'.format(instance_url_prefix),
request_method='POST')