Add basic CRUD for Person "preferred first name"

only shown if config flag says so
This commit is contained in:
Lance Edgar 2024-04-01 18:05:27 -05:00
parent cdc857065b
commit 1889f7d269
3 changed files with 91 additions and 17 deletions

View file

@ -5,6 +5,9 @@ CHANGELOG
Unreleased Unreleased
---------- ----------
* Add basic CRUD for Person "preferred first name".
0.9.89 (2024-03-27) 0.9.89 (2024-03-27)
------------------- -------------------

View file

@ -91,6 +91,12 @@
<span>{{ person.first_name }}</span> <span>{{ person.first_name }}</span>
</b-field> </b-field>
% if use_preferred_first_name:
<b-field horizontal label="Preferred First Name">
<span>{{ person.preferred_first_name }}</span>
</b-field>
% endif
<b-field horizontal label="Middle Name"> <b-field horizontal label="Middle Name">
<span>{{ person.middle_name }}</span> <span>{{ person.middle_name }}</span>
</b-field> </b-field>
@ -118,11 +124,25 @@
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
<b-field label="First Name">
<b-input v-model.trim="editNameFirst" <b-field grouped>
:maxlength="maxLengths.person_first_name || null">
</b-input> <b-field label="First Name" expanded>
<b-input v-model.trim="editNameFirst"
:maxlength="maxLengths.person_first_name || null">
</b-input>
</b-field>
% if use_preferred_first_name:
<b-field label="Preferred First Name" expanded>
<b-input v-model.trim="editNameFirstPreferred"
:maxlength="maxLengths.person_preferred_first_name || null">
</b-input>
</b-field>
% endif
</b-field> </b-field>
<b-field label="Middle Name"> <b-field label="Middle Name">
<b-input v-model.trim="editNameMiddle" <b-input v-model.trim="editNameMiddle"
:maxlength="maxLengths.person_middle_name || null"> :maxlength="maxLengths.person_middle_name || null">
@ -1497,6 +1517,9 @@
% if request.has_perm('people_profile.edit_person'): % if request.has_perm('people_profile.edit_person'):
editNameShowDialog: false, editNameShowDialog: false,
editNameFirst: null, editNameFirst: null,
% if use_preferred_first_name:
editNameFirstPreferred: null,
% endif
editNameMiddle: null, editNameMiddle: null,
editNameLast: null, editNameLast: null,
@ -1590,6 +1613,9 @@
editNameInit() { editNameInit() {
this.editNameFirst = this.person.first_name this.editNameFirst = this.person.first_name
% if use_preferred_first_name:
this.editNameFirstPreferred = this.person.preferred_first_name
% endif
this.editNameMiddle = this.person.middle_name this.editNameMiddle = this.person.middle_name
this.editNameLast = this.person.last_name this.editNameLast = this.person.last_name
this.editNameShowDialog = true this.editNameShowDialog = true
@ -1599,6 +1625,9 @@
let url = '${url('people.profile_edit_name', uuid=person.uuid)}' let url = '${url('people.profile_edit_name', uuid=person.uuid)}'
let params = { let params = {
first_name: this.editNameFirst, first_name: this.editNameFirst,
% if use_preferred_first_name:
preferred_first_name: this.editNameFirstPreferred,
% endif
middle_name: this.editNameMiddle, middle_name: this.editNameMiddle,
last_name: this.editNameLast, last_name: this.editNameLast,
} }

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -32,7 +32,8 @@ import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
import sqlalchemy_continuum as continuum import sqlalchemy_continuum as continuum
from rattail.db import model, api from rattail.db import api
from rattail.db.model import Person, PersonNote, MergePeopleRequest
from rattail.db.util import maxlen from rattail.db.util import maxlen
from rattail.time import localtime from rattail.time import localtime
from rattail.util import simple_error from rattail.util import simple_error
@ -53,7 +54,7 @@ class PersonView(MasterView):
""" """
Master view for the Person class. Master view for the Person class.
""" """
model_class = model.Person model_class = Person
model_title_plural = "People" model_title_plural = "People"
route_prefix = 'people' route_prefix = 'people'
touchable = True touchable = True
@ -210,6 +211,7 @@ class PersonView(MasterView):
c="MR") c="MR")
def get_instance(self): def get_instance(self):
model = self.model
# TODO: I don't recall why this fallback check for a vendor contact # TODO: I don't recall why this fallback check for a vendor contact
# exists here, but leaving it intact for now. # exists here, but leaving it intact for now.
key = self.request.matchdict['uuid'] key = self.request.matchdict['uuid']
@ -237,6 +239,13 @@ class PersonView(MasterView):
return True return True
return not self.is_person_protected(person) return not self.is_person_protected(person)
def configure_form(self, f):
super().configure_form(f)
# preferred_first_name
if self.people_handler.should_use_preferred_first_name():
f.insert_after('first_name', 'preferred_first_name')
def objectify(self, form, data=None): def objectify(self, form, data=None):
if data is None: if data is None:
data = form.validated data = form.validated
@ -248,6 +257,9 @@ class PersonView(MasterView):
names = {} names = {}
if 'first_name' in form: if 'first_name' in form:
names['first'] = data['first_name'] names['first'] = data['first_name']
if self.people_handler.should_use_preferred_first_name():
if 'preferred_first_name' in form:
names['preferred_first'] = data['preferred_first_name']
if 'middle_name' in form: if 'middle_name' in form:
names['middle'] = data['middle_name'] names['middle'] = data['middle_name']
if 'last_name' in form: if 'last_name' in form:
@ -292,6 +304,8 @@ class PersonView(MasterView):
In addition to "touching" the person proper, we also "touch" each In addition to "touching" the person proper, we also "touch" each
contact info record associated with them. contact info record associated with them.
""" """
model = self.model
# touch person, as per usual # touch person, as per usual
super().touch_instance(person) super().touch_instance(person)
@ -426,6 +440,7 @@ class PersonView(MasterView):
return "" return ""
def get_version_child_classes(self): def get_version_child_classes(self):
model = self.model
return [ return [
(model.PersonPhoneNumber, 'parent_uuid'), (model.PersonPhoneNumber, 'parent_uuid'),
(model.PersonEmailAddress, 'parent_uuid'), (model.PersonEmailAddress, 'parent_uuid'),
@ -474,6 +489,7 @@ class PersonView(MasterView):
'expose_customer_people': self.customers_should_expose_people(), 'expose_customer_people': self.customers_should_expose_people(),
'expose_customer_shoppers': self.customers_should_expose_shoppers(), 'expose_customer_shoppers': self.customers_should_expose_shoppers(),
'max_one_member': app.get_membership_handler().max_one_per_person(), 'max_one_member': app.get_membership_handler().max_one_per_person(),
'use_preferred_first_name': self.people_handler.should_use_preferred_first_name(),
} }
if self.request.has_perm('people_profile.view_versions'): if self.request.has_perm('people_profile.view_versions'):
@ -552,7 +568,7 @@ class PersonView(MasterView):
def get_max_lengths(self): def get_max_lengths(self):
model = self.model model = self.model
return { lengths = {
'person_first_name': maxlen(model.Person.first_name), 'person_first_name': maxlen(model.Person.first_name),
'person_middle_name': maxlen(model.Person.middle_name), 'person_middle_name': maxlen(model.Person.middle_name),
'person_last_name': maxlen(model.Person.last_name), 'person_last_name': maxlen(model.Person.last_name),
@ -562,6 +578,9 @@ class PersonView(MasterView):
'address_state': maxlen(model.PersonMailingAddress.state), 'address_state': maxlen(model.PersonMailingAddress.state),
'address_zipcode': maxlen(model.PersonMailingAddress.zipcode), 'address_zipcode': maxlen(model.PersonMailingAddress.zipcode),
} }
if self.people_handler.should_use_preferred_first_name():
lengths['person_preferred_first_name'] = maxlen(model.Person.preferred_first_name)
return lengths
def get_phone_type_options(self): def get_phone_type_options(self):
""" """
@ -606,6 +625,9 @@ class PersonView(MasterView):
'dynamic_content_title': self.get_context_content_title(person), 'dynamic_content_title': self.get_context_content_title(person),
} }
if self.people_handler.should_use_preferred_first_name():
context['preferred_first_name'] = person.preferred_first_name
if person.address: if person.address:
context['address'] = self.get_context_address(person.address) context['address'] = self.get_context_address(person.address)
@ -871,10 +893,16 @@ class PersonView(MasterView):
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
self.handler.update_names(person, kw = {
first=data['first_name'], 'first': data['first_name'],
middle=data['middle_name'], 'middle': data['middle_name'],
last=data['last_name']) 'last': data['last_name'],
}
if self.people_handler.should_use_preferred_first_name():
kw['preferred_first'] = data['preferred_first_name']
self.handler.update_names(person, **kw)
self.Session.flush() self.Session.flush()
return self.profile_changed_response(person) return self.profile_changed_response(person)
@ -913,6 +941,7 @@ class PersonView(MasterView):
""" """
View which updates a phone number for the person. View which updates a phone number for the person.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -940,6 +969,7 @@ class PersonView(MasterView):
""" """
View which allows a person's phone number to be deleted. View which allows a person's phone number to be deleted.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -960,6 +990,7 @@ class PersonView(MasterView):
""" """
View which allows a person's "preferred" phone to be set. View which allows a person's "preferred" phone to be set.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -1016,6 +1047,7 @@ class PersonView(MasterView):
""" """
View which updates an email address for the person. View which updates an email address for the person.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -1039,6 +1071,7 @@ class PersonView(MasterView):
""" """
View which allows a person's email address to be deleted. View which allows a person's email address to be deleted.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -1059,6 +1092,7 @@ class PersonView(MasterView):
""" """
View which allows a person's "preferred" email to be set. View which allows a person's "preferred" email to be set.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
data = dict(self.request.json_body) data = dict(self.request.json_body)
@ -1192,6 +1226,7 @@ class PersonView(MasterView):
""" """
AJAX view for updating an employee history record. AJAX view for updating an employee history record.
""" """
model = self.model
person = self.get_instance() person = self.get_instance()
employee = person.employee employee = person.employee
@ -1459,6 +1494,7 @@ class PersonView(MasterView):
return self.profile_changed_response(person) return self.profile_changed_response(person)
def create_note(self, person, form): def create_note(self, person, form):
model = self.model
note = model.PersonNote() note = model.PersonNote()
note.type = form.validated['note_type'] note.type = form.validated['note_type']
note.subject = form.validated['note_subject'] note.subject = form.validated['note_subject']
@ -1478,6 +1514,7 @@ class PersonView(MasterView):
return self.profile_changed_response(person) return self.profile_changed_response(person)
def update_note(self, person, form): def update_note(self, person, form):
model = self.model
note = self.Session.get(model.PersonNote, form.validated['uuid']) note = self.Session.get(model.PersonNote, form.validated['uuid'])
note.subject = form.validated['note_subject'] note.subject = form.validated['note_subject']
note.text = form.validated['note_text'] note.text = form.validated['note_text']
@ -1494,10 +1531,12 @@ class PersonView(MasterView):
return self.profile_changed_response(person) return self.profile_changed_response(person)
def delete_note(self, person, form): def delete_note(self, person, form):
model = self.model
note = self.Session.get(model.PersonNote, form.validated['uuid']) note = self.Session.get(model.PersonNote, form.validated['uuid'])
self.Session.delete(note) self.Session.delete(note)
def make_user(self): def make_user(self):
model = self.model
uuid = self.request.POST['person_uuid'] uuid = self.request.POST['person_uuid']
person = self.Session.get(model.Person, uuid) person = self.Session.get(model.Person, uuid)
if not person: if not person:
@ -1815,7 +1854,7 @@ class PersonNoteView(MasterView):
""" """
Master view for the PersonNote class. Master view for the PersonNote class.
""" """
model_class = model.PersonNote model_class = PersonNote
route_prefix = 'person_notes' route_prefix = 'person_notes'
url_prefix = '/people/notes' url_prefix = '/people/notes'
has_versions = True has_versions = True
@ -1842,6 +1881,7 @@ class PersonNoteView(MasterView):
def configure_grid(self, g): def configure_grid(self, g):
super().configure_grid(g) super().configure_grid(g)
model = self.model
# person # person
g.set_joiner('person', lambda q: q.join(model.Person, g.set_joiner('person', lambda q: q.join(model.Person,
@ -1881,7 +1921,7 @@ def valid_note_uuid(node, kw):
session = kw['session'] session = kw['session']
person_uuid = kw['person_uuid'] person_uuid = kw['person_uuid']
def validate(node, value): def validate(node, value):
note = session.get(model.PersonNote, value) note = session.get(PersonNote, value)
if not note: if not note:
raise colander.Invalid(node, "Note not found") raise colander.Invalid(node, "Note not found")
if note.person.uuid != person_uuid: if note.person.uuid != person_uuid:
@ -1906,7 +1946,7 @@ class MergePeopleRequestView(MasterView):
""" """
Master view for the MergePeopleRequest class. Master view for the MergePeopleRequest class.
""" """
model_class = model.MergePeopleRequest model_class = MergePeopleRequest
route_prefix = 'people_merge_requests' route_prefix = 'people_merge_requests'
url_prefix = '/people/merge-requests' url_prefix = '/people/merge-requests'
creatable = False creatable = False
@ -1950,8 +1990,9 @@ class MergePeopleRequestView(MasterView):
g.set_link('keeping_uuid') g.set_link('keeping_uuid')
def render_referenced_person_name(self, merge_request, field): def render_referenced_person_name(self, merge_request, field):
model = self.model
uuid = getattr(merge_request, field) uuid = getattr(merge_request, field)
person = self.Session.get(self.model.Person, uuid) person = self.Session.get(model.Person, uuid)
if person: if person:
return str(person) return str(person)
return "(person not found)" return "(person not found)"
@ -1971,8 +2012,9 @@ class MergePeopleRequestView(MasterView):
f.set_renderer('keeping_uuid', self.render_referenced_person) f.set_renderer('keeping_uuid', self.render_referenced_person)
def render_referenced_person(self, merge_request, field): def render_referenced_person(self, merge_request, field):
model = self.model
uuid = getattr(merge_request, field) uuid = getattr(merge_request, field)
person = self.Session.get(self.model.Person, uuid) person = self.Session.get(model.Person, uuid)
if person: if person:
text = str(person) text = str(person)
url = self.request.route_url('people.view', uuid=person.uuid) url = self.request.route_url('people.view', uuid=person.uuid)