diff --git a/docs/api/index.rst b/docs/api/index.rst index 65a6c875a87afba06f31a7bd5aae96134a475f0a..e266e2f51984e08adcbb40e2dcb3bc8b3db7d692 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -26,6 +26,7 @@ attributes and method signatures etc. rattail/db/model.batch.purchase rattail/db/model.batch.vendorcatalog rattail/db/model.datasync + rattail/db/model.people rattail/db/model.products rattail/db/model.purchase rattail/db/util @@ -49,6 +50,7 @@ attributes and method signatures etc. rattail/reporting/handlers rattail/logging rattail/mail + rattail/people rattail/time rattail/upgrades rattail/win32 diff --git a/docs/api/rattail/db/model.people.rst b/docs/api/rattail/db/model.people.rst new file mode 100644 index 0000000000000000000000000000000000000000..5c31c323dd44b6cf369bb30425a69eca3e487ba7 --- /dev/null +++ b/docs/api/rattail/db/model.people.rst @@ -0,0 +1,6 @@ + +``rattail.db.model.people`` +=========================== + +.. automodule:: rattail.db.model.people + :members: diff --git a/docs/api/rattail/people.rst b/docs/api/rattail/people.rst new file mode 100644 index 0000000000000000000000000000000000000000..404947b34d7c836a35f320ddddc2b92e17092737 --- /dev/null +++ b/docs/api/rattail/people.rst @@ -0,0 +1,6 @@ + +``rattail.people`` +================== + +.. automodule:: rattail.people + :members: diff --git a/rattail/app.py b/rattail/app.py index c938e34e64830606e6043d6515785c6e3c87176b..f7bad50aef0b146c42c84db1aba9d7659aa3c425 100644 --- a/rattail/app.py +++ b/rattail/app.py @@ -79,9 +79,16 @@ class AppHandler(object): return self.mail_handler def get_people_handler(self, **kwargs): + """ + Returns a reference to the configured People Handler. + + See also :doc:`rattail-manual:base/handlers/other/people`. + """ if not hasattr(self, 'people_handler'): - from rattail.people import get_people_handler - self.people_handler = get_people_handler(self.config, **kwargs) + spec = self.config.get('rattail', 'people.handler', + default='rattail.people:PeopleHandler') + factory = load_object(spec) + self.people_handler = factory(self.config, **kwargs) return self.people_handler def get_products_handler(self, **kwargs): diff --git a/rattail/people.py b/rattail/people.py index 37fb011131ae7dd017fa7de232ea8fffcf555849..09fffa438bc937ce74997a801488252e072db411 100644 --- a/rattail/people.py +++ b/rattail/people.py @@ -22,11 +22,16 @@ ################################################################################ """ People Handler + +See also :doc:`rattail-manual:base/handlers/other/people`. """ from __future__ import unicode_literals, absolute_import -from rattail.util import load_object +import warnings + +from sqlalchemy import orm + from rattail.app import GenericHandler @@ -36,6 +41,14 @@ class PeopleHandler(GenericHandler): """ def normalize_full_name(self, first, last, **kwargs): + """ + Normalize a "full" name based on the given first and last + names. Tries to be smart about collapsing whitespace etc. + + :param first: First name. + :param last: Last name. + :returns: First and last name combined. + """ from rattail.db.util import normalize_full_name return normalize_full_name(first, last) @@ -80,14 +93,84 @@ class PeopleHandler(GenericHandler): # person.set_primary_address(address) return address + def get_merge_preview_fields(self, **kwargs): + """ + Returns a sequence of fields which will be used during a merge + preview. + """ + def F(name, **kwargs): + field = {'name': name} + field.update(kwargs) + return field + + return [ + F('uuid'), + F('first_name'), + F('last_name'), + F('display_name'), + F('usernames', additive=True), + F('member_uuids', additive=True), + ] + + def get_merge_preview_data(self, person, **kwargs): + """ + Must return a data dictionary for the given person, which can + be presented to the user during a merge preview. + """ + return { + 'uuid': person.uuid, + 'first_name': person.first_name, + 'last_name': person.last_name, + 'display_name': person.display_name, + 'usernames': [u.username for u in person.users], + 'member_uuids': [m.uuid for m in person.members], + } + + def why_not_merge(self, removing, keeping, **kwargs): + """ + Evaluate the given merge candidates and if there is a reason *not* + to merge them, return that reason. + + :param removing: Person record which will be removed, should the + merge happen. + :param keeping: Person record which will be kept, should the + merge happen. + :returns: String indicating reason not to merge, or ``None``. + """ + + def perform_merge(self, removing, keeping, **kwargs): + """ + Perform an actual merge of the 2 given people. + + :param removing: Person record which should be removed. + :param keeping: Person record which should be kept. + """ + # move Member records to final Person + for member in list(removing.members): + removing.members.remove(member) + keeping.members.append(member) + + # move User records to final Person + for user in list(removing.users): + removing.users.remove(user) + keeping.users.append(user) + + # delete unwanted Person + session = orm.object_session(keeping) + session.delete(removing) + session.flush() + def get_people_handler(config, **kwargs): """ Create and return the configured :class:`PeopleHandler` instance. + + .. warning:: + This function is deprecated; please use + :meth:`~rattail.app.AppHandler.get_people_handler` instead. """ - spec = config.get('rattail', 'people.handler') - if spec: - factory = load_object(spec) - else: - factory = PeopleHandler - return factory(config, **kwargs) + warnings.warn("get_people_handler() function is deprecated, " + "please use app.get_people_handler() method instead", + DeprecationWarning) + app = config.get_app() + return app.get_people_handler(**kwargs)