diff --git a/CHANGES.rst b/CHANGES.rst
index 38c3b959..1d8d63d3 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,9 @@ CHANGELOG
Unreleased
----------
+* Add basic CRUD for Person "preferred first name".
+
+
0.9.89 (2024-03-27)
-------------------
diff --git a/tailbone/templates/people/view_profile_buefy.mako b/tailbone/templates/people/view_profile_buefy.mako
index 4b1e089c..81243464 100644
--- a/tailbone/templates/people/view_profile_buefy.mako
+++ b/tailbone/templates/people/view_profile_buefy.mako
@@ -91,6 +91,12 @@
{{ person.first_name }}
+ % if use_preferred_first_name:
+
+ {{ person.preferred_first_name }}
+
+ % endif
+
{{ person.middle_name }}
@@ -118,11 +124,25 @@
-
-
-
+
+
+
+
+
+
+
+
+ % if use_preferred_first_name:
+
+
+
+
+ % endif
+
+
@@ -1497,6 +1517,9 @@
% if request.has_perm('people_profile.edit_person'):
editNameShowDialog: false,
editNameFirst: null,
+ % if use_preferred_first_name:
+ editNameFirstPreferred: null,
+ % endif
editNameMiddle: null,
editNameLast: null,
@@ -1590,6 +1613,9 @@
editNameInit() {
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.editNameLast = this.person.last_name
this.editNameShowDialog = true
@@ -1599,6 +1625,9 @@
let url = '${url('people.profile_edit_name', uuid=person.uuid)}'
let params = {
first_name: this.editNameFirst,
+ % if use_preferred_first_name:
+ preferred_first_name: this.editNameFirstPreferred,
+ % endif
middle_name: this.editNameMiddle,
last_name: this.editNameLast,
}
diff --git a/tailbone/views/people.py b/tailbone/views/people.py
index 7f786ace..071e58b5 100644
--- a/tailbone/views/people.py
+++ b/tailbone/views/people.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@@ -32,7 +32,8 @@ import sqlalchemy as sa
from sqlalchemy import orm
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.time import localtime
from rattail.util import simple_error
@@ -53,7 +54,7 @@ class PersonView(MasterView):
"""
Master view for the Person class.
"""
- model_class = model.Person
+ model_class = Person
model_title_plural = "People"
route_prefix = 'people'
touchable = True
@@ -210,6 +211,7 @@ class PersonView(MasterView):
c="MR")
def get_instance(self):
+ model = self.model
# TODO: I don't recall why this fallback check for a vendor contact
# exists here, but leaving it intact for now.
key = self.request.matchdict['uuid']
@@ -237,6 +239,13 @@ class PersonView(MasterView):
return True
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):
if data is None:
data = form.validated
@@ -248,6 +257,9 @@ class PersonView(MasterView):
names = {}
if 'first_name' in form:
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:
names['middle'] = data['middle_name']
if 'last_name' in form:
@@ -292,6 +304,8 @@ class PersonView(MasterView):
In addition to "touching" the person proper, we also "touch" each
contact info record associated with them.
"""
+ model = self.model
+
# touch person, as per usual
super().touch_instance(person)
@@ -426,6 +440,7 @@ class PersonView(MasterView):
return ""
def get_version_child_classes(self):
+ model = self.model
return [
(model.PersonPhoneNumber, 'parent_uuid'),
(model.PersonEmailAddress, 'parent_uuid'),
@@ -474,6 +489,7 @@ class PersonView(MasterView):
'expose_customer_people': self.customers_should_expose_people(),
'expose_customer_shoppers': self.customers_should_expose_shoppers(),
'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'):
@@ -552,7 +568,7 @@ class PersonView(MasterView):
def get_max_lengths(self):
model = self.model
- return {
+ lengths = {
'person_first_name': maxlen(model.Person.first_name),
'person_middle_name': maxlen(model.Person.middle_name),
'person_last_name': maxlen(model.Person.last_name),
@@ -562,6 +578,9 @@ class PersonView(MasterView):
'address_state': maxlen(model.PersonMailingAddress.state),
'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):
"""
@@ -606,6 +625,9 @@ class PersonView(MasterView):
'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:
context['address'] = self.get_context_address(person.address)
@@ -871,10 +893,16 @@ class PersonView(MasterView):
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'])
+ kw = {
+ 'first': data['first_name'],
+ 'middle': data['middle_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()
return self.profile_changed_response(person)
@@ -913,6 +941,7 @@ class PersonView(MasterView):
"""
View which updates a phone number for the person.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -940,6 +969,7 @@ class PersonView(MasterView):
"""
View which allows a person's phone number to be deleted.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -960,6 +990,7 @@ class PersonView(MasterView):
"""
View which allows a person's "preferred" phone to be set.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -1016,6 +1047,7 @@ class PersonView(MasterView):
"""
View which updates an email address for the person.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -1039,6 +1071,7 @@ class PersonView(MasterView):
"""
View which allows a person's email address to be deleted.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -1059,6 +1092,7 @@ class PersonView(MasterView):
"""
View which allows a person's "preferred" email to be set.
"""
+ model = self.model
person = self.get_instance()
data = dict(self.request.json_body)
@@ -1192,6 +1226,7 @@ class PersonView(MasterView):
"""
AJAX view for updating an employee history record.
"""
+ model = self.model
person = self.get_instance()
employee = person.employee
@@ -1459,6 +1494,7 @@ class PersonView(MasterView):
return self.profile_changed_response(person)
def create_note(self, person, form):
+ model = self.model
note = model.PersonNote()
note.type = form.validated['note_type']
note.subject = form.validated['note_subject']
@@ -1478,6 +1514,7 @@ class PersonView(MasterView):
return self.profile_changed_response(person)
def update_note(self, person, form):
+ model = self.model
note = self.Session.get(model.PersonNote, form.validated['uuid'])
note.subject = form.validated['note_subject']
note.text = form.validated['note_text']
@@ -1494,10 +1531,12 @@ class PersonView(MasterView):
return self.profile_changed_response(person)
def delete_note(self, person, form):
+ model = self.model
note = self.Session.get(model.PersonNote, form.validated['uuid'])
self.Session.delete(note)
def make_user(self):
+ model = self.model
uuid = self.request.POST['person_uuid']
person = self.Session.get(model.Person, uuid)
if not person:
@@ -1815,7 +1854,7 @@ class PersonNoteView(MasterView):
"""
Master view for the PersonNote class.
"""
- model_class = model.PersonNote
+ model_class = PersonNote
route_prefix = 'person_notes'
url_prefix = '/people/notes'
has_versions = True
@@ -1842,6 +1881,7 @@ class PersonNoteView(MasterView):
def configure_grid(self, g):
super().configure_grid(g)
+ model = self.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']
person_uuid = kw['person_uuid']
def validate(node, value):
- note = session.get(model.PersonNote, value)
+ note = session.get(PersonNote, value)
if not note:
raise colander.Invalid(node, "Note not found")
if note.person.uuid != person_uuid:
@@ -1906,7 +1946,7 @@ class MergePeopleRequestView(MasterView):
"""
Master view for the MergePeopleRequest class.
"""
- model_class = model.MergePeopleRequest
+ model_class = MergePeopleRequest
route_prefix = 'people_merge_requests'
url_prefix = '/people/merge-requests'
creatable = False
@@ -1950,8 +1990,9 @@ class MergePeopleRequestView(MasterView):
g.set_link('keeping_uuid')
def render_referenced_person_name(self, merge_request, field):
+ model = self.model
uuid = getattr(merge_request, field)
- person = self.Session.get(self.model.Person, uuid)
+ person = self.Session.get(model.Person, uuid)
if person:
return str(person)
return "(person not found)"
@@ -1971,8 +2012,9 @@ class MergePeopleRequestView(MasterView):
f.set_renderer('keeping_uuid', self.render_referenced_person)
def render_referenced_person(self, merge_request, field):
+ model = self.model
uuid = getattr(merge_request, field)
- person = self.Session.get(self.model.Person, uuid)
+ person = self.Session.get(model.Person, uuid)
if person:
text = str(person)
url = self.request.route_url('people.view', uuid=person.uuid)